From bee627d00190d8077c538445a1eb96cfa111f9eb Mon Sep 17 00:00:00 2001 From: Danilo Date: Tue, 21 Mar 2017 23:45:04 +0100 Subject: [PATCH] AirPlay --- AVPlayerOverlay.podspec | 2 +- AVPlayerOverlay.xcodeproj/project.pbxproj | 3 + AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.h | 25 +- AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.m | 250 ++++++++++++++++-- AVPlayerOverlay/AVPlayer/AVPlayerVC.m | 4 +- AVPlayerOverlay/AVPlayer/SubtitlePackage.m | 8 +- .../icon_airplay.imageset/Contents.json | 23 ++ .../icon_airplay.imageset/icon_airplay.png | Bin 0 -> 286 bytes .../icon_airplay.imageset/icon_airplay@2x.png | Bin 0 -> 478 bytes .../icon_airplay.imageset/icon_airplay@3x.png | Bin 0 -> 565 bytes AVPlayerOverlay/Base.lproj/Main.storyboard | 46 +++- 11 files changed, 334 insertions(+), 27 deletions(-) create mode 100644 AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/Contents.json create mode 100644 AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay.png create mode 100644 AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay@2x.png create mode 100644 AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay@3x.png diff --git a/AVPlayerOverlay.podspec b/AVPlayerOverlay.podspec index 3478da2..5abda58 100644 --- a/AVPlayerOverlay.podspec +++ b/AVPlayerOverlay.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AVPlayerOverlay' - s.version = '1.6' + s.version = '1.7' s.summary = 'AVPlayer with custom controls and full screen features.' s.license = 'MIT' s.authors = { 'Danilo Priore' => 'support@prioregroup.com' } diff --git a/AVPlayerOverlay.xcodeproj/project.pbxproj b/AVPlayerOverlay.xcodeproj/project.pbxproj index 4153022..c016eac 100644 --- a/AVPlayerOverlay.xcodeproj/project.pbxproj +++ b/AVPlayerOverlay.xcodeproj/project.pbxproj @@ -147,6 +147,7 @@ TargetAttributes = { 9F16AFEA1CD3E3980082CC15 = { CreatedOnToolsVersion = 7.3; + DevelopmentTeam = JQRB98RUML; }; }; }; @@ -311,6 +312,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = JQRB98RUML; INFOPLIST_FILE = AVPlayerOverlay/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.prioregroup.AVPlayerOverlay; @@ -322,6 +324,7 @@ isa = XCBuildConfiguration; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = JQRB98RUML; INFOPLIST_FILE = AVPlayerOverlay/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.prioregroup.AVPlayerOverlay; diff --git a/AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.h b/AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.h index ea3e40d..dde45bb 100644 --- a/AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.h +++ b/AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.h @@ -4,8 +4,13 @@ // Created by Danilo Priore on 28/04/16. // Copyright © 2016 Prioregroup.com. All rights reserved. // -#define AVPlayerOverlayVCFullScreenNotification @"AVPlayerOverlayVCFullScreen" -#define AVPlayerOverlayVCNormalScreenNotification @"AVPlayerOverlayVCNormalScreen" +#define AVPlayerOverlayVCFullScreenNotification @"AVPlayerOverlayVCFullScreen" +#define AVPlayerOverlayVCNormalScreenNotification @"AVPlayerOverlayVCNormalScreen" +#define AVPlayerOverlayVCAirPlayInUseNotification @"AVPlayerOverlayVCAirPlayInUse" +#define AVPlayerOverlayVCAirPlayBecomePresentNotification @"AVPlayerOverlayVCAirPlayBecomePresent" +#define AVPlayerOverlayVCAirPlayResignPresentNotification @"AVPlayerOverlayVCAirPlayResignPresent" + +#define kAVPlayerOverlayVCAirPlayInUse @"airPlayInUse" @import UIKit; @@ -25,10 +30,13 @@ IB_DESIGNABLE @property (nonatomic, weak) IBOutlet UIButton *playBigButton; @property (nonatomic, weak) IBOutlet UIButton *volumeButton; @property (nonatomic, weak) IBOutlet UIButton *fullscreenButton; +@property (nonatomic, weak) IBOutlet UIButton *airPlayButton; @property (nonatomic, weak) IBOutlet UIButton *subtitlesButton; @property (nonatomic, weak) IBOutlet UISlider *videoSlider; @property (nonatomic, weak) IBOutlet UISlider *volumeSlider; @property (nonatomic, weak) IBOutlet UILabel *subtitlesLabel; +@property (nonatomic, weak) IBOutlet UILabel *currentTimeLabel; +@property (nonatomic, weak) IBOutlet UILabel *durationTimeLabel; @property (nonatomic, weak) AVPlayer *player; @@ -36,6 +44,10 @@ IB_DESIGNABLE @property (nonatomic, assign) IBInspectable AVPlayerFullscreenAutorotaionMode autorotationMode; @property (nonatomic, assign, readonly) BOOL isFullscreen; +@property (nonatomic, assign, readonly) BOOL isAirplayInUse; +@property (nonatomic, assign, readonly) BOOL isAirplayPresent; + +@property (nonatomic, strong, readonly) NSString *airPlayPlayerName; - (void)updateProgressBar; @@ -68,6 +80,15 @@ IB_DESIGNABLE - (NSAttributedString*)attributedSubtitle:(id)subtitle; +- (void)setupAirPlay; +- (void)deallocAirplay; +- (void)airPlayRouteChange:(NSNotification*)note; +- (BOOL)checkAirPlayIsRunning; +- (void)airPlayChangeInUseState:(BOOL)isInUse; + +- (void)airplayBecomePresent; +- (void)airplayResignPresent; + - (void)forceDeviceOrientation:(UIInterfaceOrientation)orientation; - (void)deviceOrientationDidChange:(NSNotification *)notification; diff --git a/AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.m b/AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.m index 53ac7d2..9a31c6c 100644 --- a/AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.m +++ b/AVPlayerOverlay/AVPlayer/AVPlayerOverlayVC.m @@ -16,6 +16,7 @@ @interface AVPlayerOverlayVC () @property (nonatomic, weak) UIWindow *mainWindow; @property (nonatomic, weak) UIViewController *mainParent; @property (nonatomic, weak) UISlider *mpSlider; +@property (nonatomic, weak) UIButton *airPlayInternalButton; @property (nonatomic, assign) CGRect currentFrame; @@ -23,7 +24,9 @@ @interface AVPlayerOverlayVC () @property (nonatomic, strong) MPVolumeView *volume; @property (nonatomic, strong) id timeObserver; + @property (nonatomic, assign) BOOL isVideoSliderMoving; +@property (nonatomic, assign) BOOL isAirPlayRoutingVisible; @property (nonatomic, assign) BOOL hiddenStatusBar; @property (nonatomic, assign) BOOL hiddenNavBar; @@ -36,6 +39,8 @@ @interface AVPlayerOverlayVC () @implementation AVPlayerOverlayVC +static void *AirPlayContext = &AirPlayContext; + - (instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { @@ -57,7 +62,7 @@ - (void)viewDidLoad self.view.backgroundColor = [UIColor clearColor]; // system volume - self.volume = [[MPVolumeView alloc] initWithFrame:CGRectZero]; + _volume = [[MPVolumeView alloc] initWithFrame:CGRectZero]; for (id view in _volume.subviews) { if ([view isKindOfClass:[UISlider class]]) { _mpSlider = view; @@ -69,6 +74,8 @@ - (void)viewDidLoad _volumeSlider.transform = CGAffineTransformMakeRotation(-M_PI_2); _volumeSlider.value = [AVAudioSession sharedInstance].outputVolume; + _playButton.enabled = NO; + _playBigButton.enabled = NO; _playBigButton.layer.borderWidth = 1.0; _playBigButton.layer.borderColor = [UIColor whiteColor].CGColor; _playBigButton.layer.cornerRadius = _playBigButton.frame.size.width / 2.0; @@ -77,8 +84,10 @@ - (void)viewDidLoad _subtitlesLabel.numberOfLines = 0; _subtitlesLabel.contentMode = UIViewContentModeBottom; _subtitlesLabel.baselineAdjustment = UIBaselineAdjustmentAlignBaselines; + _subtitlesButton.enabled = NO; [self videoSliderEnabled:NO]; + [self setupAirPlay]; // actions [_playButton addTarget:self action:@selector(didPlayButtonSelected:) forControlEvents:UIControlEventTouchUpInside]; @@ -89,6 +98,7 @@ - (void)viewDidLoad [_videoSlider addTarget:self action:@selector(didVideoSliderTouchDown:) forControlEvents:UIControlEventTouchDown]; [_volumeSlider addTarget:self action:@selector(didVolumeSliderValueChanged:) forControlEvents:UIControlEventValueChanged]; [_subtitlesButton addTarget:self action:@selector(didSubtitlesButtonSelected:) forControlEvents:UIControlEventTouchUpInside]; + [_airPlayButton addTarget:self action:@selector(didAirPlayButtonSelected:) forControlEvents:UIControlEventTouchUpInside]; // tap gesture for hide/show player bar UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapGesture:)]; @@ -142,14 +152,26 @@ - (void)setPlayer:(AVPlayer *)player _volumeSlider.value = 1.0; _videoSlider.value = 0.0; + _playButton.enabled = NO; + _playBigButton.enabled = NO; } else { + if (_durationTimeLabel) { + CMTime duration = [self playerItemDuration]; + _durationTimeLabel.text = CMTIME_IS_VALID(duration) ? [_subtitles makeSaveName:duration] : nil; + } + __weak typeof(self) wself = self; self.timeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.5, NSEC_PER_SEC) queue:NULL usingBlock:^(CMTime time){ + [wself updateProgressBar]; + + if (wself.currentTimeLabel) + wself.currentTimeLabel.text = CMTIME_IS_VALID(time) ? [wself.subtitles makeSaveName:time] : nil; + if (!wself.subtitlesLabel.hidden && wself.subtitles.subtitleItems.count > 0) { NSInteger index = [wself.subtitles indexOfProperSubtitleWithGivenCMTime:time]; @@ -157,9 +179,12 @@ - (void)setPlayer:(AVPlayer *)player wself.subtitlesLabel.attributedText = [wself attributedSubtitle:subtitle]; [wself.subtitlesLabel setNeedsDisplay]; } + }]; _videoSlider.value = 0; _volumeSlider.value = _player.volume; + _playButton.enabled = YES; + _playBigButton.enabled = YES; } _playButton.selected = NO; @@ -167,7 +192,6 @@ - (void)setPlayer:(AVPlayer *)player _playBigButton.selected = NO; [self showPlayerBar]; - [self autoHidePlayerBar]; [self videoSliderEnabled:NO]; } } @@ -192,35 +216,64 @@ - (void)setConstraintValue:(CGFloat)value animations:(void(^)())animations completion:(void(^)(BOOL finished))completion { - NSArray *constraints = self.view.constraints; - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d", attribute]; - NSArray *filteredArray = [constraints filteredArrayUsingPredicate:predicate]; - NSLayoutConstraint *constraint = [filteredArray firstObject]; - if (constraint.constant != value) { - [self.view removeConstraint:constraint]; - constraint.constant = value; - [self.view addConstraint:constraint]; + dispatch_async(dispatch_get_main_queue(), ^{ - [UIView animateWithDuration:duration animations:^{ - if (animations) - animations(); - [self.view layoutIfNeeded]; - } completion:completion]; + NSArray *constraints = self.view.constraints; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"firstAttribute = %d", attribute]; + NSArray *filteredArray = [constraints filteredArrayUsingPredicate:predicate]; + NSLayoutConstraint *constraint = [filteredArray firstObject]; + if (constraint.constant != value) { + [self.view removeConstraint:constraint]; + constraint.constant = value; + [self.view addConstraint:constraint]; + + [UIView animateWithDuration:duration animations:^{ + if (animations) + animations(); + [self.view layoutIfNeeded]; + } completion:completion]; + } + }); +} + +#pragma mark - Observe + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (context == AirPlayContext) { + if (object == _airPlayInternalButton && [[change valueForKey:NSKeyValueChangeNewKey] intValue] == 1) { + // airplayIsPresent + _isAirplayPresent = YES; + [self airplayBecomePresent]; + } + else { + _isAirplayPresent = NO; + [self airplayResignPresent]; + } + } + else + { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; } } + #pragma mark - PlayerBar - (void)autoHidePlayerBar { + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hidePlayerBar) object:nil]; + if (_playBarAutoideInterval > 0) { - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hidePlayerBar) object:nil]; [self performSelector:@selector(hidePlayerBar) withObject:nil afterDelay:_playBarAutoideInterval]; } } - (void)hidePlayerBar { + if (_isAirplayInUse || _isAirPlayRoutingVisible) + return; + CGFloat height = _playerBarView.frame.size.height; __weak typeof(self) wself = self; @@ -241,7 +294,7 @@ - (void)showPlayerBar { _playerBarView.hidden = NO; _playBigButton.hidden = NO; - + __weak typeof(self) wself = self; [self setConstraintValue:0 forAttribute:NSLayoutAttributeBottom @@ -410,6 +463,12 @@ - (void)didSubtitlesButtonSelected:(id)sender [self hideSubtitles]; } +- (void)didAirPlayButtonSelected:(id)sender +{ + [_airPlayInternalButton sendActionsForControlEvents:UIControlEventTouchUpInside]; + [self checkAirPlayRoutingViewVisible]; +} + #pragma mark - Overridable Methods - (void)willFullScreenModeFromParentViewController:(UIViewController*)parent @@ -539,6 +598,7 @@ - (void)loadSubtitlesWithURL:(NSURL*)url if (error == nil) { NSString *context = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; _subtitles = [[SubtitlePackage alloc] initWithContext:context]; + _subtitlesButton.enabled = _subtitles.subtitleItems.count > 0; } }]; @@ -578,6 +638,161 @@ - (NSAttributedString*)attributedSubtitle:(IndividualSubtitle*)subtitle return attrString; } +#pragma mark - Player Helper + +- (CMTime)playerItemDuration +{ + if (_player.currentItem && _player.currentItem.asset) { + if (_player.currentItem.status == AVPlayerItemStatusReadyToPlay && !CMTIME_IS_INDEFINITE(_player.currentItem.asset.duration)) { + return _player.currentItem.asset.duration; + } + } + + return kCMTimeInvalid; +} + +#pragma mark - AirPlay + +- (void)setupAirPlay +{ + if (_airPlayButton) + { + _volume.hidden = YES; + [_airPlayButton insertSubview:_volume atIndex:0]; // important! + + _airPlayButton.enabled = NO; + for (id current in _volume.subviews) + { + if([current isKindOfClass:[UIButton class]]) + { + _airPlayInternalButton = (UIButton*)current; + [_airPlayInternalButton addObserver:self forKeyPath:@"alpha" options:NSKeyValueObservingOptionNew context:AirPlayContext]; + break; + } + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(airPlayRouteChange:) + name:AVAudioSessionRouteChangeNotification + object:nil]; + } +} + +- (void)deallocAirplay +{ + @try { + [_airPlayInternalButton removeObserver:self forKeyPath:@"alpha"]; + } @catch (NSException *exception) { + // NOP + } + + [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil ]; + + _isAirplayInUse = NO; +} + +- (void)airPlayRouteChange:(NSNotification*)note +{ + BOOL inUse = [self checkAirPlayIsRunning]; + [self airPlayChangeInUseState:inUse]; +} + +- (BOOL)checkAirPlayIsRunning +{ + BOOL isActive = NO; + + NSArray *outputs = [AVAudioSession sharedInstance].currentRoute.outputs; + for (AVAudioSessionPortDescription *outItem in outputs) + { + if([outItem.portType isEqualToString:AVAudioSessionPortAirPlay]) { + _airPlayPlayerName = outItem.portName; + isActive = YES; + break; + } + } + + return isActive; +} + +- (void)airPlayChangeInUseState:(BOOL)isInUse +{ + dispatch_async(dispatch_get_main_queue(), ^{ + if(isInUse) + { + _isAirplayInUse = YES; + _airPlayButton.selected = YES; + + if(_subtitlesLabel && _subtitles.subtitleItems.count > 0) { + _subtitlesButton.enabled = NO; + [self hideSubtitles]; + } + } + else + { + _isAirplayInUse = NO; + _airPlayButton.selected = NO; + + if(_subtitlesLabel && _subtitles.subtitleItems.count > 0 && _subtitlesButton.selected) { + _subtitlesButton.enabled = YES; + [self showSubtitles]; + } + } + + [self showPlayerBar]; + + [[NSNotificationCenter defaultCenter] postNotificationName:AVPlayerOverlayVCAirPlayInUseNotification + object:self + userInfo:@{kAVPlayerOverlayVCAirPlayInUse : @(_isAirplayInUse)}]; + }); +} + +- (void)checkAirPlayRoutingViewVisible +{ + static BOOL routingVisible = NO; + + UIWindow *window = [UIApplication sharedApplication].keyWindow; + _isAirPlayRoutingVisible = [self isAirPlayRoutingInView:window]; + if (_isAirPlayRoutingVisible != routingVisible) { + routingVisible = _isAirPlayRoutingVisible; + if (routingVisible == NO) { + [self autoHidePlayerBar]; + return; + } + } + + [self performSelector:@selector(checkAirPlayRoutingViewVisible) withObject:nil afterDelay:1]; +} + +- (BOOL)isAirPlayRoutingInView:(UIView *)view +{ + BOOL ret = NO; + for (id subview in view.subviews) + { + NSString *className = NSStringFromClass([subview class]); + if ([className hasPrefix:@"MPAVRouting"]) { + ret = YES; + break; + } else { + ret = [self isAirPlayRoutingInView:subview]; + if (ret) break; + } + } + + return ret; +} + +- (void)airplayBecomePresent +{ + _airPlayButton.enabled = YES; + [[NSNotificationCenter defaultCenter] postNotificationName:AVPlayerOverlayVCAirPlayBecomePresentNotification object:self]; +} + +- (void)airplayResignPresent +{ + _airPlayButton.enabled = NO; + [[NSNotificationCenter defaultCenter] postNotificationName:AVPlayerOverlayVCAirPlayResignPresentNotification object:self]; +} + #pragma mark - Device rotation - (void)forceDeviceOrientation:(UIInterfaceOrientation)orientation @@ -608,4 +823,5 @@ - (void)deviceOrientationDidChange:(NSNotification *)notification }]; } + @end diff --git a/AVPlayerOverlay/AVPlayer/AVPlayerVC.m b/AVPlayerOverlay/AVPlayer/AVPlayerVC.m index 5de2801..fdd418a 100644 --- a/AVPlayerOverlay/AVPlayer/AVPlayerVC.m +++ b/AVPlayerOverlay/AVPlayer/AVPlayerVC.m @@ -100,10 +100,10 @@ - (void)setVideoURL:(NSURL *)videoURL @synchronized (self) { _videoURL = videoURL; - if (_videoURL) { + if (_videoURL != nil) { AVAsset *asset = [self.player.currentItem asset]; - if (_videoURL != nil && [asset isKindOfClass:[AVURLAsset class]]) { + if ([asset isKindOfClass:[AVURLAsset class]]) { NSURL *current_url = [(AVURLAsset*)asset URL]; if ([current_url.absoluteString isEqualToString:_videoURL.absoluteString]) return; diff --git a/AVPlayerOverlay/AVPlayer/SubtitlePackage.m b/AVPlayerOverlay/AVPlayer/SubtitlePackage.m index ec60fbe..cc3c210 100755 --- a/AVPlayerOverlay/AVPlayer/SubtitlePackage.m +++ b/AVPlayerOverlay/AVPlayer/SubtitlePackage.m @@ -219,7 +219,8 @@ - (NSString *)makeSaveName:(CMTime)time { hour=[NSString stringWithFormat:@"0%d-",(int)timeInSecond/3600]; } else{ - hour=@"00-"; + //hour=@"00-"; + hour=@" "; } NSString *min; @@ -237,6 +238,7 @@ - (NSString *)makeSaveName:(CMTime)time { sec=[NSString stringWithFormat:@"%d-",(int)timeInSecond%3600%60]; } + /* float fract=(timeInSecond-(int)timeInSecond)*100; NSString *fra; if (fract<10) { @@ -245,8 +247,10 @@ - (NSString *)makeSaveName:(CMTime)time { fra=[NSString stringWithFormat:@"%d",(int)fract]; } - NSString *saveName=[[[hour stringByAppendingString:min] stringByAppendingString:sec] stringByAppendingString:fra]; + */ + + NSString *saveName=[[hour stringByAppendingString:min] stringByAppendingString:sec]; return saveName; } diff --git a/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/Contents.json b/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/Contents.json new file mode 100644 index 0000000..e446d3d --- /dev/null +++ b/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon_airplay.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icon_airplay@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "icon_airplay@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay.png b/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay.png new file mode 100644 index 0000000000000000000000000000000000000000..0f9eb765f635f43a68b075136b8e2adba60cb75d GIT binary patch literal 286 zcmV+(0pb3MP)U|39J71N7!^0cix()9Maeve@E$ ks#o#YvB}U5k80Gq1Ly)4I9^=lKmY&$07*qoM6N<$f~(MV$^ZZW literal 0 HcmV?d00001 diff --git a/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay@2x.png b/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..27d0b36c4c939e30e8551afe33c7d936678f0bed GIT binary patch literal 478 zcmV<40U`d0P)x!p>~40i*qL^pncqG$nLP71(BVfz?--b%rA>|ibJ~2X>}}>3fDSQ(%0`Ti4)Biv zx_Sxp9?)+<6M(MXggSr@paaMrM3_mCz^u_A!bFA?i9A;pKv02m1| zJHPH{gfOZL5N3L{U`z?C2tb^{&5ko5t`Y!IhIiMjAyE|o@H0lZ3m`DYFAZQomd}SC z!+CDKAYJwkSIs?Lf`9FZlbA@TR;n-5{HE*NUr#@K UWTJx+jQ{`u07*qoM6N<$g0Pm)zW@LL literal 0 HcmV?d00001 diff --git a/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay@3x.png b/AVPlayerOverlay/Assets.xcassets/icon_airplay.imageset/icon_airplay@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ad7f7bcafaaeabd00277069d9f091cacd1ba0997 GIT binary patch literal 565 zcmV-50?Pe~P)g;lTOxhyRUm#~@_uF2aDYWguwl=tu}6Xh92F(1I4U zpam^xK?_>Yf)=!(1ubYn3tG^Ewu&Z0CS+)m+;dMdL}*s1GGRiMm5`uuxcnY9T;hZS zeZu=U`km7dpgEix>>LIhO^T-}^b@H7qe;`9IYjG{4k(%!w|~z5ZixjD%_`-&Bg-2$~!(u7~k2v!oknGX-if;4l zdOeU;4qM`!MRU%QvQw4^4~lk#G`p@>il}l_k=KRMuN0M?qNF@17HtV|DDZ}#yiw5E zYr8>j(8F(fY}h1k(n3dCY}!Lt(3OfE@G@6Sw4o}@4EOy= z$s~E75hgNX-xPUULz(-#{B4^cuW^IP+_0vb9HpkQX3VZ5HML35fxf^-F7$yNiiU6b z=eCQQ`VkKJz*asu&_Ui|h|LVy@k`#KgY9(Kny>l-F* - + @@ -153,21 +153,57 @@ + + + + - + + + + - + + +