Updating and adding a few APIs for ASVideoNode

This commit is contained in:
Max Gu
2016-05-18 08:40:58 -07:00
parent 772cf15b83
commit a93bc72e25
2 changed files with 82 additions and 30 deletions

View File

@@ -14,9 +14,9 @@
typedef enum { typedef enum {
ASVideoNodePlayerStateUnknown, ASVideoNodePlayerStateUnknown,
ASVideoNodePlayerStatePlaying, ASVideoNodePlayerStateInitialLoading,
ASVideoNodePlayerStateStartupLoading,
ASVideoNodePlayerStateLoading, ASVideoNodePlayerStateLoading,
ASVideoNodePlayerStatePlaying,
ASVideoNodePlayerStatePaused, ASVideoNodePlayerStatePaused,
ASVideoNodePlayerStateFinished ASVideoNodePlayerStateFinished
} ASVideoNodePlayerState; } ASVideoNodePlayerState;
@@ -48,6 +48,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign, readwrite) BOOL shouldAutorepeat; @property (nonatomic, assign, readwrite) BOOL shouldAutorepeat;
@property (nonatomic, assign, readwrite) BOOL muted; @property (nonatomic, assign, readwrite) BOOL muted;
@property (nonatomic, assign, readwrite) BOOL shouldAggressivelyRecoverFromStall;
@property (nonatomic, assign, readonly) ASVideoNodePlayerState playerState; @property (nonatomic, assign, readonly) ASVideoNodePlayerState playerState;
//! Defaults to 100 //! Defaults to 100
@@ -69,26 +70,26 @@ NS_ASSUME_NONNULL_BEGIN
* @abstract Delegate method invoked when the node's video has played to its end time. * @abstract Delegate method invoked when the node's video has played to its end time.
* @param videoNode The video node has played to its end time. * @param videoNode The video node has played to its end time.
*/ */
- (void)videoPlaybackDidFinish:(ASVideoNode *)videoNode; - (void)videoDidPlayToEnd:(ASVideoNode *)videoNode;
/** /**
* @abstract Delegate method invoked the node is tapped. * @abstract Delegate method invoked the node is tapped.
* @param videoNode The video node that was tapped. * @param videoNode The video node that was tapped.
* @discussion The video's play state is toggled if this method is not implemented. * @discussion The video's play state is toggled if this method is not implemented.
*/ */
- (void)videoNodeWasTapped:(ASVideoNode *)videoNode; - (void)didTapVideoNode:(ASVideoNode *)videoNode;
/** /**
* @abstract Delegate method invoked when player changes state. * @abstract Delegate method invoked when player changes state.
* @param videoNode The video node that was tapped. * @param videoNode The video node.
* @param state player state before this change. * @param state player state before this change.
* @param toSate player new state. * @param toState player new state.
* @discussion This method is called after each state change * @discussion This method is called after each state change
*/ */
- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toSate; - (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState;
/** /**
* @abstract Ssks delegate if state change is allowed * @abstract Ssks delegate if state change is allowed
* ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused. * ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused.
* asks delegate if state change is allowed. * asks delegate if state change is allowed.
* @param videoNode The video node that was tapped. * @param videoNode The video node.
* @param state player state that is going to be set. * @param state player state that is going to be set.
* @discussion Delegate method invoked when player changes it's state to * @discussion Delegate method invoked when player changes it's state to
* ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused * ASVideoNodePlayerStatePlaying or ASVideoNodePlayerStatePaused
@@ -97,10 +98,32 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)videoNode:(ASVideoNode*)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state; - (BOOL)videoNode:(ASVideoNode*)videoNode shouldChangePlayerStateTo:(ASVideoNodePlayerState)state;
/** /**
* @abstract Delegate method invoked when player playback time is updated. * @abstract Delegate method invoked when player playback time is updated.
* @param videoNode The video node that was tapped. * @param videoNode The video node.
* @param second current playback time in seconds. * @param second current playback time in seconds.
*/ */
- (void)videoNode:(ASVideoNode *)videoNode didPlayToSecond:(NSTimeInterval)second; - (void)videoNode:(ASVideoNode *)videoNode didPlayToTimeInterval:(NSTimeInterval)timeInterval;
/**
* @abstract Delegate method invoked when the video player stalls.
* @param videoNode The video node that has experienced the stall
* @param second Current playback time when the stall happens
*/
- (void)videoNode:(ASVideoNode *)videoNode didStallAtTimeInterval:(NSTimeInterval)timeInterval;
/**
* @abstract Delegate method invoked when the video player starts the inital asset loading
* @param videoNode The videoNode
*/
- (void)videoNodeDidStartInitialLoading:(ASVideoNode *)videoNode;
/**
* @abstract Delegate method invoked when the video is done loading the asset and can start the playback
* @param videoNode The videoNode
*/
- (void)videoNodeDidFinishInitialLoading:(ASVideoNode *)videoNode;
/**
* @abstract Delegate method invoked when the video node has recovered from the stall
* @param videoNode The videoNode
*/
- (void)videoNodeDidRecoverFromStall:(ASVideoNode *)videoNode;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
#endif #endif

View File

@@ -38,17 +38,21 @@ static NSString * const kStatus = @"status";
__weak id<ASVideoNodeDelegate> _delegate; __weak id<ASVideoNodeDelegate> _delegate;
struct { struct {
unsigned int delegateVideNodeShouldChangePlayerStateTo:1; unsigned int delegateVideNodeShouldChangePlayerStateTo:1;
unsigned int delegateVideoPlaybackDidFinish:1; unsigned int delegateVideoDidPlayToEnd:1;
unsigned int delegateVideoNodeWasTapped:1; unsigned int delegateDidTapVideoNode:1;
unsigned int delegateVideoNodeWillChangePlayerStateToState:1; unsigned int delegateVideoNodeWillChangePlayerStateToState:1;
unsigned int delegateVideoNodeDidPlayToSecond:1; unsigned int delegateVideoNodeDidPlayToTimeInterval:1;
unsigned int delegateVideoNodeDidStartInitialLoading:1;
unsigned int delegateVideoNodeDidFinishInitialLoading:1;
unsigned int delegateVideoNodeDidStallAtTimeInterval:1;
unsigned int delegateVideoNodeDidRecoverFromStall:1;
} _delegateFlags; } _delegateFlags;
BOOL _shouldBePlaying; BOOL _shouldBePlaying;
BOOL _shouldAutorepeat; BOOL _shouldAutorepeat;
BOOL _shouldAutoplay; BOOL _shouldAutoplay;
BOOL _shouldAggressivelyRecoverFromStall;
BOOL _muted; BOOL _muted;
ASVideoNodePlayerState _playerState; ASVideoNodePlayerState _playerState;
@@ -164,6 +168,7 @@ static NSString * const kStatus = @"status";
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:@selector(didPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem]; [notificationCenter addObserver:self selector:@selector(didPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
[notificationCenter addObserver:self selector:@selector(videoNodeDidStall:) name:AVPlayerItemPlaybackStalledNotification object:playerItem];
[notificationCenter addObserver:self selector:@selector(errorWhilePlaying:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:playerItem]; [notificationCenter addObserver:self selector:@selector(errorWhilePlaying:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:playerItem];
[notificationCenter addObserver:self selector:@selector(errorWhilePlaying:) name:AVPlayerItemNewErrorLogEntryNotification object:playerItem]; [notificationCenter addObserver:self selector:@selector(errorWhilePlaying:) name:AVPlayerItemNewErrorLogEntryNotification object:playerItem];
} }
@@ -181,6 +186,7 @@ static NSString * const kStatus = @"status";
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem]; [notificationCenter removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
[notificationCenter removeObserver:self name: AVPlayerItemPlaybackStalledNotification object:playerItem];
[notificationCenter removeObserver:self name:AVPlayerItemFailedToPlayToEndTimeNotification object:playerItem]; [notificationCenter removeObserver:self name:AVPlayerItemFailedToPlayToEndTimeNotification object:playerItem];
[notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem]; [notificationCenter removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:playerItem];
} }
@@ -313,12 +319,12 @@ static NSString * const kStatus = @"status";
if (_placeholderImageNode.image == nil) { if (_placeholderImageNode.image == nil) {
[self generatePlaceholderImage]; [self generatePlaceholderImage];
} }
if (_shouldBePlaying) {
self.playerState = ASVideoNodePlayerStatePlaying;
}
} }
} else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUpKey]) { } else if ([keyPath isEqualToString:kPlaybackLikelyToKeepUpKey]) {
if (_shouldBePlaying && [change[NSKeyValueChangeNewKey] boolValue] == true && ASInterfaceStateIncludesVisible(self.interfaceState)) { if (_shouldBePlaying && (_shouldAggressivelyRecoverFromStall || [change[NSKeyValueChangeNewKey] boolValue]) && ASInterfaceStateIncludesVisible(self.interfaceState)) {
if (self.playerState == ASVideoNodePlayerStateLoading && _delegateFlags.delegateVideoNodeDidRecoverFromStall) {
[_delegate videoNodeDidRecoverFromStall:self];
}
[self play]; // autoresume after buffer catches up [self play]; // autoresume after buffer catches up
} }
} else if ([keyPath isEqualToString:kplaybackBufferEmpty]) { } else if ([keyPath isEqualToString:kplaybackBufferEmpty]) {
@@ -331,8 +337,8 @@ static NSString * const kStatus = @"status";
- (void)tapped - (void)tapped
{ {
if (_delegateFlags.delegateVideoNodeWasTapped) { if (_delegateFlags.delegateDidTapVideoNode) {
[_delegate videoNodeWasTapped:self]; [_delegate didTapVideoNode:self];
} else { } else {
if (_shouldBePlaying) { if (_shouldBePlaying) {
[self pause]; [self pause];
@@ -349,10 +355,20 @@ static NSString * const kStatus = @"status";
{ {
ASDN::MutexLocker l(_videoLock); ASDN::MutexLocker l(_videoLock);
AVAsset *asset = self.asset; AVAsset *asset = self.asset;
// Return immediately if the asset is nil;
if (asset == nil || self.playerState == ASVideoNodePlayerStateInitialLoading) {
return;
}
NSArray<NSString *> *requestedKeys = @[@"playable"]; NSArray<NSString *> *requestedKeys = @[@"playable"];
self.playerState = ASVideoNodePlayerStateStartupLoading; self.playerState = ASVideoNodePlayerStateInitialLoading;
if (_delegateFlags.delegateVideoNodeDidStartInitialLoading) {
[_delegate videoNodeDidStartInitialLoading:self];
}
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:^{ [asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:^{
ASPerformBlockOnMainThread(^{ ASPerformBlockOnMainThread(^{
if (_delegateFlags.delegateVideoNodeDidFinishInitialLoading) {
[_delegate videoNodeDidFinishInitialLoading:self];
}
[self prepareToPlayAsset:asset withKeys:requestedKeys]; [self prepareToPlayAsset:asset withKeys:requestedKeys];
}); });
}]; }];
@@ -366,8 +382,8 @@ static NSString * const kStatus = @"status";
return; return;
} }
if (_delegateFlags.delegateVideoNodeDidPlayToSecond) { if (_delegateFlags.delegateVideoNodeDidPlayToTimeInterval) {
[_delegate videoNode:self didPlayToSecond:timeInSeconds]; [_delegate videoNode:self didPlayToTimeInterval:timeInSeconds];
} }
} }
@@ -482,10 +498,14 @@ static NSString * const kStatus = @"status";
memset(&_delegateFlags, 0, sizeof(_delegateFlags)); memset(&_delegateFlags, 0, sizeof(_delegateFlags));
} else { } else {
_delegateFlags.delegateVideNodeShouldChangePlayerStateTo = [_delegate respondsToSelector:@selector(videoNode:shouldChangePlayerStateTo:)]; _delegateFlags.delegateVideNodeShouldChangePlayerStateTo = [_delegate respondsToSelector:@selector(videoNode:shouldChangePlayerStateTo:)];
_delegateFlags.delegateVideoPlaybackDidFinish = [_delegate respondsToSelector:@selector(videoPlaybackDidFinish:)]; _delegateFlags.delegateVideoDidPlayToEnd = [_delegate respondsToSelector:@selector(videoDidPlayToEnd:)];
_delegateFlags.delegateVideoNodeWasTapped = [_delegate respondsToSelector:@selector(videoNodeWasTapped:)]; _delegateFlags.delegateDidTapVideoNode = [_delegate respondsToSelector:@selector(didTapVideoNode:)];
_delegateFlags.delegateVideoNodeWillChangePlayerStateToState = [_delegate respondsToSelector:@selector(videoNode:willChangePlayerState:toState:)]; _delegateFlags.delegateVideoNodeWillChangePlayerStateToState = [_delegate respondsToSelector:@selector(videoNode:willChangePlayerState:toState:)];
_delegateFlags.delegateVideoNodeDidPlayToSecond = [_delegate respondsToSelector:@selector(videoNode:didPlayToSecond:)]; _delegateFlags.delegateVideoNodeDidPlayToTimeInterval = [_delegate respondsToSelector:@selector(videoNode:didPlayToTimeInterval:)];
_delegateFlags.delegateVideoNodeDidStartInitialLoading = [_delegate respondsToSelector:@selector(videoNodeDidStartInitialLoading:)];
_delegateFlags.delegateVideoNodeDidFinishInitialLoading = [_delegate respondsToSelector:@selector(videoNodeDidFinishInitialLoading:)];
_delegateFlags.delegateVideoNodeDidStallAtTimeInterval = [_delegate respondsToSelector:@selector(videoNode:didStallAtTimeInterval:)];
_delegateFlags.delegateVideoNodeDidRecoverFromStall = [_delegate respondsToSelector:@selector(videoNodeDidRecoverFromStall:)];
} }
} }
@@ -632,8 +652,8 @@ static NSString * const kStatus = @"status";
- (void)didPlayToEnd:(NSNotification *)notification - (void)didPlayToEnd:(NSNotification *)notification
{ {
self.playerState = ASVideoNodePlayerStateFinished; self.playerState = ASVideoNodePlayerStateFinished;
if (_delegateFlags.delegateVideoPlaybackDidFinish) { if (_delegateFlags.delegateVideoDidPlayToEnd) {
[_delegate videoPlaybackDidFinish:self]; [_delegate videoDidPlayToEnd:self];
} }
[_player seekToTime:kCMTimeZero]; [_player seekToTime:kCMTimeZero];
@@ -644,6 +664,15 @@ static NSString * const kStatus = @"status";
} }
} }
- (void)videoNodeDidStall:(NSNotification *)notification
{
self.playerState = ASVideoNodePlayerStateLoading;
[self showSpinner];
if (_delegateFlags.delegateVideoNodeDidStallAtTimeInterval) {
[_delegate videoNode:self didStallAtTimeInterval:CMTimeGetSeconds(_player.currentItem.currentTime)];
}
}
- (void)errorWhilePlaying:(NSNotification *)notification - (void)errorWhilePlaying:(NSNotification *)notification
{ {
if ([notification.name isEqualToString:AVPlayerItemFailedToPlayToEndTimeNotification]) { if ([notification.name isEqualToString:AVPlayerItemFailedToPlayToEndTimeNotification]) {