added delegate method for video did finish, moved layer creation to after view displays, rearranged spinner logic, added tests

This commit is contained in:
Luke Parham
2015-12-22 15:34:11 -06:00
parent e318285089
commit 6dc15ffd44
5 changed files with 194 additions and 85 deletions

View File

@@ -7,25 +7,28 @@ typedef NS_ENUM(NSUInteger, ASVideoGravity) {
ASVideoGravityResize
};
@protocol ASVideoNodeDelegate;
@interface ASVideoNode : ASControlNode<_ASDisplayLayerDelegate>
@property (atomic, strong, readwrite) AVAsset *asset;
@property (atomic, strong, readonly) AVPlayer *player;
@property (atomic, strong, readonly) AVPlayerItem *currentItem;
@property (nonatomic, assign, readwrite) BOOL shouldAutoplay;
@property (atomic) ASVideoGravity gravity;
@property (atomic) BOOL autorepeat;
@property (atomic) ASButtonNode *playButton;
@property (atomic) AVPlayer *player;
@property (atomic, weak, readwrite) id<ASVideoNodeDelegate> delegate;
- (void)play;
- (void)pause;
- (BOOL)isPlaying;
@end
@protocol ASVideoNodeDelegate <NSObject>
@optional
- (void)videoDidReachEnd:(ASVideoNode *)videoNode;
@end
@protocol ASVideoNodeDataSource <NSObject>
@optional
- (ASButtonNode *)playButtonForVideoNode:(ASVideoNode *)videoNode;
- (UIImage *)thumbnailForVideoNode:(ASVideoNode *) videoNode;
- (NSURL *)thumbnailURLForVideoNode:(ASVideoNode *)videoNode;
@end

View File

@@ -6,14 +6,18 @@
{
ASDN::RecursiveMutex _lock;
__weak id<ASVideoNodeDataSource> _dataSource;
__weak id<ASVideoNodeDelegate> _delegate;
BOOL _shouldBePlaying;
AVAsset *_asset;
AVPlayerItem *_currentItem;
AVPlayer *_player;
ASButtonNode *_playButton;
ASDisplayNode *_playerNode;
ASDisplayNode *_spinner;
ASVideoGravity _gravity;
}
@end
@@ -28,13 +32,6 @@
{
if (!(self = [super init])) { return nil; }
_playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
playerLayer.player = [[AVPlayer alloc] init];
return playerLayer;
}];
[self addSubnode:_playerNode];
self.gravity = ASVideoGravityResizeAspect;
[self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
@@ -51,12 +48,17 @@
if (!(newState & ASInterfaceStateVisible)) {
[self pause];
[(UIActivityIndicatorView *)_spinner.view stopAnimating];
[_spinner removeFromSupernode];
} else {
if (_shouldBePlaying) {
[self play];
}
}
if (newState & ASInterfaceStateVisible) {
[self displayDidFinish];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
@@ -76,7 +78,8 @@
- (void)didPlayToEnd:(NSNotification *)notification
{
if (ASObjectIsEqual([[notification object] asset], _asset)) {
[[((AVPlayerLayer *)_playerNode.layer) player] seekToTime:CMTimeMakeWithSeconds(0, 1)];
[_delegate videoDidReachEnd:self];
[_player seekToTime:CMTimeMakeWithSeconds(0, 1)];
if (_autorepeat) {
[self play];
@@ -90,6 +93,12 @@
{
[super layout];
_playerNode.frame = self.bounds;
CGFloat horizontalDiff = (self.bounds.size.width - _playButton.bounds.size.width)/2;
CGFloat verticalDiff = (self.bounds.size.height - _playButton.bounds.size.height)/2;
_playButton.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);
_spinner.frame = _playButton.frame;
}
- (void)tapped
@@ -123,18 +132,47 @@
_currentItem = [[AVPlayerItem alloc] initWithAsset:_asset];
[_currentItem addObserver:self forKeyPath:NSStringFromSelector(@selector(status)) options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL];
if (((AVPlayerLayer *)_playerNode.layer).player) {
[((AVPlayerLayer *)_playerNode.layer).player replaceCurrentItemWithPlayerItem:_currentItem];
if (_player) {
[_player replaceCurrentItemWithPlayerItem:_currentItem];
} else {
((AVPlayerLayer *)_playerNode.layer).player = [[AVPlayer alloc] initWithPlayerItem:_currentItem];
_player = [[AVPlayer alloc] initWithPlayerItem:_currentItem];
}
}
}
// FIXME: Adopt interfaceStateDidChange API
- (void)displayDidFinish
{
[super displayDidFinish];
ASDN::MutexLocker l(_lock);
_playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
playerLayer.player = _player;
playerLayer.videoGravity = [self videoGravity];
return playerLayer;
}];
[self insertSubnode:_playerNode atIndex:0];
if (_shouldAutoplay) {
[self play];
}
}
- (NSString *)videoGravity
{
if (_gravity == ASVideoGravityResize) {
return AVLayerVideoGravityResize;
}
if (_gravity == ASVideoGravityResizeAspectFill) {
return AVLayerVideoGravityResizeAspectFill;
}
return AVLayerVideoGravityResizeAspect;
}
- (void)clearFetchedData
{
[super clearFetchedData];
@@ -142,10 +180,13 @@
{
ASDN::MutexLocker l(_lock);
((AVPlayerLayer *)_playerNode.layer).player = nil;
_player = nil;
_shouldBePlaying = NO;
}
}
#pragma mark - Video Properties
- (void)setPlayButton:(ASButtonNode *)playButton
{
ASDN::MutexLocker l(_lock);
@@ -174,6 +215,7 @@
_asset = asset;
// FIXME: Adopt -setNeedsFetchData when it is available
if (self.interfaceState & ASInterfaceStateFetchData) {
[self fetchData];
}
@@ -185,6 +227,12 @@
return _asset;
}
- (AVPlayer *)player
{
ASDN::MutexLocker l(_lock);
return _player;
}
- (void)setGravity:(ASVideoGravity)gravity
{
ASDN::MutexLocker l(_lock);
@@ -203,77 +251,92 @@
((AVPlayerLayer *)_playerNode.layer).videoGravity = AVLayerVideoGravityResizeAspect;
break;
}
_gravity = gravity;
}
- (ASVideoGravity)gravity
{
ASDN::MutexLocker l(_lock);
if (ASObjectIsEqual(((AVPlayerLayer *)_playerNode.layer).contentsGravity, AVLayerVideoGravityResize)) {
return ASVideoGravityResize;
}
if (ASObjectIsEqual(((AVPlayerLayer *)_playerNode.layer).contentsGravity, AVLayerVideoGravityResizeAspectFill)) {
return ASVideoGravityResizeAspectFill;
}
return ASVideoGravityResizeAspect;
return _gravity;
}
#pragma mark - Video Playback
- (void)play
{
ASDN::MutexLocker l(_lock);
if (!_spinner) {
_spinner = [[ASDisplayNode alloc] initWithViewBlock:^UIView *{
UIActivityIndicatorView *spinnnerView = [[UIActivityIndicatorView alloc] initWithFrame:_playButton.frame];
UIActivityIndicatorView *spinnnerView = [[UIActivityIndicatorView alloc] init];
spinnnerView.color = [UIColor whiteColor];
[spinnnerView startAnimating];
return spinnnerView;
}];
}
if (![self ready]) {
[self addSubnode:_spinner];
}
[[((AVPlayerLayer *)_playerNode.layer) player] play];
[_player play];
_shouldBePlaying = YES;
_playButton.alpha = 0.0;
// if ([self ready] && ![self.subnodes containsObject:_spinner]) {
// }
if (![self ready] && _shouldBePlaying && (self.interfaceState & ASInterfaceStateVisible)) {
[self addSubnode:_spinner];
[(UIActivityIndicatorView *)_spinner.view startAnimating];
}
}
- (BOOL)ready
{
return [((AVPlayerLayer *)_playerNode.layer) player].currentItem.status == AVPlayerItemStatusReadyToPlay;
return _currentItem.status == AVPlayerItemStatusReadyToPlay;
}
- (void)pause
{
ASDN::MutexLocker l(_lock);
[[((AVPlayerLayer *)_playerNode.layer) player] pause];
[_player pause];
[((UIActivityIndicatorView *)_spinner.view) stopAnimating];
_shouldBePlaying = NO;
_playButton.alpha = 1.0;
}
- (AVPlayerItem *)currentItem
- (BOOL)isPlaying
{
return _currentItem;
ASDN::MutexLocker l(_lock);
return (_player.rate > 0 && !_player.error);
}
#pragma mark - Property Accessors for Tests
- (ASDisplayNode *)spinner
{
ASDN::MutexLocker l(_lock);
return _spinner;
}
- (AVPlayerItem *)curentItem
{
ASDN::MutexLocker l(_lock);
return _currentItem;
}
- (ASDisplayNode *)playerNode
{
ASDN::MutexLocker l(_lock);
return _playerNode;
}
- (BOOL)shouldBePlaying
{
ASDN::MutexLocker l(_lock);
return _shouldBePlaying;
}
#pragma mark - Lifecycle
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];