Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

fixes memory leaks and allows for a bit more customization #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions HSPlayerView.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,12 @@

// Hides statusBar if true, defaults to YES
@property (nonatomic, assign) BOOL fullScreen;

// Defaults to YES
@property (nonatomic, assign) BOOL shouldAutoplay;
// Defaults to NO
@property (nonatomic, assign) BOOL shouldAutorepeat;
// Defaults to YES
@property (nonatomic, assign) BOOL shouldShowControlsOnTouch;

@end
94 changes: 77 additions & 17 deletions HSPlayerView.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import <QuartzCore/QuartzCore.h>
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>
#import <Availability.h>

// Constants
CGFloat const HSPlayerViewControlsAnimationDelay = .4f; // ~ statusbar fade duration
Expand Down Expand Up @@ -62,6 +63,8 @@ @interface HSPlayerView () <UIGestureRecognizerDelegate>
// Custom images for controls
@property (nonatomic, strong) UIImage *playImage;
@property (nonatomic, strong) UIImage *pauseImage;

@property (nonatomic, strong) NSMutableDictionary *observations;
@end

@implementation HSPlayerView
Expand Down Expand Up @@ -96,11 +99,18 @@ - (id)initWithFrame:(CGRect)frame {
[self setFullScreen:YES];

[self setRestoreAfterScrubbingRate:1.];

[self setShouldAutoplay:YES];
[self setShouldShowControlsOnTouch:YES];

_observations = [NSMutableDictionary new];
}

return self;
}



#pragma mark KVO

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
Expand All @@ -122,7 +132,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N

// Enable buttons & scrubber

if (!self.isScrubbing)
if (!self.isScrubbing && self.shouldAutoplay)
[self setPlaying:YES];
}
break;
Expand Down Expand Up @@ -186,6 +196,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N
}

- (void)dealloc {

[self removePlayerTimeObserver];

[self.player removeObserver:self forKeyPath:@"rate"];
Expand All @@ -194,10 +205,14 @@ - (void)dealloc {
[self.playerItem removeObserver:self forKeyPath:@"duration"];
[self.playerLayer removeObserver:self forKeyPath:@"readyForDisplay"];

if ([self.playerItem respondsToSelector:@selector(allowsAirPlayVideo)])
[self.playerItem removeObserver:self forKeyPath:@"airPlayVideoActive"];

for (NSString *key in self.observations.allKeys) {
id object = [self.observations objectForKey:key];
[object removeObserver:self forKeyPath:key];
}

[self.player pause];

[[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark - Properties
Expand All @@ -208,16 +223,39 @@ - (AVPlayer *)player {

- (void)setPlayer:(AVPlayer *)player {
[(AVPlayerLayer *) [self layer] setPlayer:player];


[self addObserverForActiveExternalPlayWithPlayer:player];
}


- (void)addObserverForActiveExternalPlayWithPlayer:(AVPlayer *)player
{
// Optimize for airplay if possible
if ([player respondsToSelector:@selector(allowsAirPlayVideo)]) {
SEL selector;
NSString *keyPath;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0
selector = @selector(allowsAirPlayVideo);
keyPath = @"airPlayVideoActive";
#else
selector = @selector(allowsExternalPlayback);
keyPath = @"externalPlaybackActive";
#endif

if ([player respondsToSelector:selector]) {
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0
[player setAllowsAirPlayVideo:YES];
[player setUsesAirPlayVideoWhileAirPlayScreenIsActive:YES];

#else
[player setAllowsExternalPlayback:YES];
[player setUsesExternalPlaybackWhileExternalScreenIsActive:YES];
#endif

[player addObserver:self
forKeyPath:@"airPlayVideoActive"
forKeyPath:keyPath
options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)
context:HSPlayerViewPlayerAirPlayVideoActiveObservationContext];

[self.observations setObject:player forKey:keyPath];
}
}

Expand Down Expand Up @@ -327,7 +365,11 @@ - (UILabel *)currentPlayerTimeLabel {
[_currentPlayerTimeLabel setBackgroundColor:[UIColor clearColor]];
[_currentPlayerTimeLabel setTextColor:[UIColor whiteColor]];
[_currentPlayerTimeLabel setFont:[UIFont systemFontOfSize:12.]];
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0
[_currentPlayerTimeLabel setTextAlignment:UITextAlignmentCenter];
#else
[_currentPlayerTimeLabel setTextAlignment:NSTextAlignmentCenter];
#endif
}

return _currentPlayerTimeLabel;
Expand All @@ -339,10 +381,14 @@ - (UILabel *)remainingPlayerTimeLabel {
[_remainingPlayerTimeLabel setBackgroundColor:[UIColor clearColor]];
[_remainingPlayerTimeLabel setTextColor:[UIColor whiteColor]];
[_remainingPlayerTimeLabel setFont:[UIFont systemFontOfSize:12.]];
[_remainingPlayerTimeLabel setTextAlignment:UITextAlignmentCenter];
[_remainingPlayerTimeLabel setAutoresizingMask:(UIViewAutoresizingFlexibleLeftMargin)];
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0
[_remainingPlayerTimeLabel setTextAlignment:UITextAlignmentCenter];
#else
[_remainingPlayerTimeLabel setTextAlignment:NSTextAlignmentCenter];
#endif
}

return _remainingPlayerTimeLabel;
}

Expand Down Expand Up @@ -458,6 +504,8 @@ - (void)cancelAutoHideControlsTimer {
}

- (void)toggleControlsWithRecognizer:(UIGestureRecognizer *)recognizer {
if (!self.shouldShowControlsOnTouch) return;

[self setControlsVisible:(!self.controlsVisible) animated:YES];

if (self.controlsVisible && self.playing)
Expand Down Expand Up @@ -493,6 +541,7 @@ - (void)doneLoadingAsset:(AVAsset *)asset withKeys:(NSArray *)keys {
// Remove observer from old playerItem and create new one
if (self.playerItem) {
[self.playerItem removeObserver:self forKeyPath:@"status"];
[self.playerItem removeObserver:self forKeyPath:@"duration"];

[[NSNotificationCenter defaultCenter] removeObserver:self
name:AVPlayerItemDidPlayToEndTimeNotification
Expand All @@ -512,12 +561,12 @@ - (void)doneLoadingAsset:(AVAsset *)asset withKeys:(NSArray *)keys {
forKeyPath:@"duration"
options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)
context:HSPlayerViewPlaterItemDurationObservationContext];
[[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerItem
queue:nil usingBlock:^(NSNotification *note) {
[self setSeekToZeroBeforePlay:YES];
}];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didPlayToEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerItem];


[self setSeekToZeroBeforePlay:YES];

Expand Down Expand Up @@ -615,7 +664,18 @@ - (void)syncScrobber {
[self.scrubberControlSlider setMaximumValue:duration];
[self.scrubberControlSlider setValue:currentSeconds];

NSLog(@"%@", self.player.currentItem.seekableTimeRanges);
// NSLog(@"%@", self.player.currentItem.seekableTimeRanges);
}


#pragma mark - NSNotification

- (void)didPlayToEnd:(NSNotification *)notification
{
[self setSeekToZeroBeforePlay:YES];
if (self.shouldAutorepeat) {
[self setPlaying:YES];
}
}

#pragma mark - UIGestureRecognizerDelegate
Expand Down