[ASVideoNode, ASVideoPlayerNode] Add video composition and audio mix capabilities (#1800)

* [ASVideoNode] Add delegate method called when the currentItem is set.

* [ASVideoNode] Add videoComposition and audioMix properties to the ASVideoNode.

* [ASVideoPlayerNode] Add new initialiser with videoComposition and audioMix, and forward new delegate method.

* [ASVideoPlayerNode] Forward missing ASVideoNodeDelegate methods.
This commit is contained in:
Flo
2016-07-10 20:32:21 +02:00
committed by appleguy
parent 10447998af
commit 2e65339f33
4 changed files with 178 additions and 7 deletions

View File

@@ -12,7 +12,7 @@
#import <AsyncDisplayKit/ASButtonNode.h> #import <AsyncDisplayKit/ASButtonNode.h>
#import <AsyncDisplayKit/ASNetworkImageNode.h> #import <AsyncDisplayKit/ASNetworkImageNode.h>
@class AVAsset, AVPlayer, AVPlayerItem; @class AVAsset, AVPlayer, AVPlayerItem, AVVideoComposition, AVAudioMix;
@protocol ASVideoNodeDelegate; @protocol ASVideoNodeDelegate;
typedef enum { typedef enum {
@@ -41,6 +41,8 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isPlaying; - (BOOL)isPlaying;
@property (nullable, atomic, strong, readwrite) AVAsset *asset; @property (nullable, atomic, strong, readwrite) AVAsset *asset;
@property (nullable, atomic, strong, readwrite) AVVideoComposition *videoComposition;
@property (nullable, atomic, strong, readwrite) AVAudioMix *audioMix;
@property (nullable, atomic, strong, readonly) AVPlayer *player; @property (nullable, atomic, strong, readonly) AVPlayer *player;
@property (nullable, atomic, strong, readonly) AVPlayerItem *currentItem; @property (nullable, atomic, strong, readonly) AVPlayerItem *currentItem;
@@ -121,6 +123,12 @@ NS_ASSUME_NONNULL_BEGIN
* @param videoNode The videoNode * @param videoNode The videoNode
*/ */
- (void)videoNodeDidFinishInitialLoading:(ASVideoNode *)videoNode; - (void)videoNodeDidFinishInitialLoading:(ASVideoNode *)videoNode;
/**
* @abstract Delegate method invoked when the AVPlayerItem for the asset has been set up and can be accessed throught currentItem.
* @param videoNode The videoNode.
* @param currentItem The AVPlayerItem that was constructed from the asset.
*/
- (void)videoNode:(ASVideoNode *)videoNode didSetCurrentItem:(AVPlayerItem *)currentItem;
/** /**
* @abstract Delegate method invoked when the video node has recovered from the stall * @abstract Delegate method invoked when the video node has recovered from the stall
* @param videoNode The videoNode * @param videoNode The videoNode

View File

@@ -49,6 +49,7 @@ static NSString * const kStatus = @"status";
unsigned int delegateVideoNodeDidPlayToTimeInterval:1; unsigned int delegateVideoNodeDidPlayToTimeInterval:1;
unsigned int delegateVideoNodeDidStartInitialLoading:1; unsigned int delegateVideoNodeDidStartInitialLoading:1;
unsigned int delegateVideoNodeDidFinishInitialLoading:1; unsigned int delegateVideoNodeDidFinishInitialLoading:1;
unsigned int delegateVideoNodeDidSetCurrentItem:1;
unsigned int delegateVideoNodeDidStallAtTimeInterval:1; unsigned int delegateVideoNodeDidStallAtTimeInterval:1;
unsigned int delegateVideoNodeDidRecoverFromStall:1; unsigned int delegateVideoNodeDidRecoverFromStall:1;
@@ -68,6 +69,8 @@ static NSString * const kStatus = @"status";
ASVideoNodePlayerState _playerState; ASVideoNodePlayerState _playerState;
AVAsset *_asset; AVAsset *_asset;
AVVideoComposition *_videoComposition;
AVAudioMix *_audioMix;
AVPlayerItem *_currentPlayerItem; AVPlayerItem *_currentPlayerItem;
AVPlayer *_player; AVPlayer *_player;
@@ -124,7 +127,10 @@ static NSString * const kStatus = @"status";
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
if (_asset != nil) { if (_asset != nil) {
return [[AVPlayerItem alloc] initWithAsset:_asset]; AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:_asset];
playerItem.videoComposition = _videoComposition;
playerItem.audioMix = _audioMix;
return playerItem;
} }
return nil; return nil;
@@ -154,6 +160,10 @@ static NSString * const kStatus = @"status";
self.player = [AVPlayer playerWithPlayerItem:playerItem]; self.player = [AVPlayer playerWithPlayerItem:playerItem];
} }
if (_delegateFlags.delegateVideoNodeDidSetCurrentItem) {
[_delegate videoNode:self didSetCurrentItem:playerItem];
}
if (self.image == nil && self.URL == nil) { if (self.image == nil && self.URL == nil) {
[self generatePlaceholderImage]; [self generatePlaceholderImage];
} }
@@ -448,6 +458,34 @@ static NSString * const kStatus = @"status";
return _asset; return _asset;
} }
- (void)setVideoComposition:(AVVideoComposition *)videoComposition
{
ASDN::MutexLocker l(_propertyLock);
_videoComposition = videoComposition;
_currentPlayerItem.videoComposition = videoComposition;
}
- (AVVideoComposition *)videoComposition
{
ASDN::MutexLocker l(_propertyLock);
return _videoComposition;
}
- (void)setAudioMix:(AVAudioMix *)audioMix
{
ASDN::MutexLocker l(_propertyLock);
_audioMix = audioMix;
_currentPlayerItem.audioMix = audioMix;
}
- (AVAudioMix *)audioMix
{
ASDN::MutexLocker l(_propertyLock);
return _audioMix;
}
- (AVPlayer *)player - (AVPlayer *)player
{ {
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
@@ -473,6 +511,7 @@ static NSString * const kStatus = @"status";
_delegateFlags.delegateVideoNodeDidPlayToTimeInterval = [_delegate respondsToSelector:@selector(videoNode:didPlayToTimeInterval:)]; _delegateFlags.delegateVideoNodeDidPlayToTimeInterval = [_delegate respondsToSelector:@selector(videoNode:didPlayToTimeInterval:)];
_delegateFlags.delegateVideoNodeDidStartInitialLoading = [_delegate respondsToSelector:@selector(videoNodeDidStartInitialLoading:)]; _delegateFlags.delegateVideoNodeDidStartInitialLoading = [_delegate respondsToSelector:@selector(videoNodeDidStartInitialLoading:)];
_delegateFlags.delegateVideoNodeDidFinishInitialLoading = [_delegate respondsToSelector:@selector(videoNodeDidFinishInitialLoading:)]; _delegateFlags.delegateVideoNodeDidFinishInitialLoading = [_delegate respondsToSelector:@selector(videoNodeDidFinishInitialLoading:)];
_delegateFlags.delegateVideoNodeDidSetCurrentItem = [_delegate respondsToSelector:@selector(videoNode:didSetCurrentItem:)];
_delegateFlags.delegateVideoNodeDidStallAtTimeInterval = [_delegate respondsToSelector:@selector(videoNode:didStallAtTimeInterval:)]; _delegateFlags.delegateVideoNodeDidStallAtTimeInterval = [_delegate respondsToSelector:@selector(videoNode:didStallAtTimeInterval:)];
_delegateFlags.delegateVideoNodeDidRecoverFromStall = [_delegate respondsToSelector:@selector(videoNodeDidRecoverFromStall:)]; _delegateFlags.delegateVideoNodeDidRecoverFromStall = [_delegate respondsToSelector:@selector(videoNodeDidRecoverFromStall:)];

View File

@@ -58,8 +58,10 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithUrl:(NSURL*)url; - (instancetype)initWithUrl:(NSURL*)url;
- (instancetype)initWithAsset:(AVAsset*)asset; - (instancetype)initWithAsset:(AVAsset*)asset;
- (instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix;
- (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible; - (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible;
- (instancetype)initWithAsset:(AVAsset *)asset loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible; - (instancetype)initWithAsset:(AVAsset *)asset loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible;
- (instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible;
#pragma mark - Public API #pragma mark - Public API
- (void)seekToTime:(CGFloat)percentComplete; - (void)seekToTime:(CGFloat)percentComplete;
@@ -156,10 +158,43 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* @abstract Delegate method invoked when the ASVideoNode has played to its end time. * @abstract Delegate method invoked when the ASVideoNode has played to its end time.
* @param videoPlayerNode The video node has played to its end time. * @param videoPlayer The video node has played to its end time.
*/ */
- (void)videoPlayerNodeDidPlayToEnd:(ASVideoPlayerNode *)videoPlayer; - (void)videoPlayerNodeDidPlayToEnd:(ASVideoPlayerNode *)videoPlayer;
/**
* @abstract Delegate method invoked when the ASVideoNode has constructed its AVPlayerItem for the asset.
* @param videoPlayer The video player node.
* @param currentItem The AVPlayerItem that was constructed from the asset.
*/
- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didSetCurrentItem:(AVPlayerItem *)currentItem;
/**
* @abstract Delegate method invoked when the ASVideoNode stalls.
* @param videoPlayer The video player node that has experienced the stall
* @param second Current playback time when the stall happens
*/
- (void)videoPlayerNode:(ASVideoPlayerNode *)videoPlayer didStallAtTimeInterval:(NSTimeInterval)timeInterval;
/**
* @abstract Delegate method invoked when the ASVideoNode starts the inital asset loading
* @param videoPlayer The videoPlayer
*/
- (void)videoPlayerNodeDidStartInitialLoading:(ASVideoPlayerNode *)videoPlayer;
/**
* @abstract Delegate method invoked when the ASVideoNode is done loading the asset and can start the playback
* @param videoPlayer The videoPlayer
*/
- (void)videoPlayerNodeDidFinishInitialLoading:(ASVideoPlayerNode *)videoPlayer;
/**
* @abstract Delegate method invoked when the ASVideoNode has recovered from the stall
* @param videoPlayer The videoplayer
*/
- (void)videoPlayerNodeDidRecoverFromStall:(ASVideoPlayerNode *)videoPlayer;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
#endif #endif

View File

@@ -37,10 +37,17 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
unsigned int delegateVideoNodeShouldChangeState:1; unsigned int delegateVideoNodeShouldChangeState:1;
unsigned int delegateVideoNodePlaybackDidFinish:1; unsigned int delegateVideoNodePlaybackDidFinish:1;
unsigned int delegateDidTapVideoPlayerNode:1; unsigned int delegateDidTapVideoPlayerNode:1;
unsigned int delegateVideoPlayerNodeDidSetCurrentItem:1;
unsigned int delegateVideoPlayerNodeDidStallAtTimeInterval:1;
unsigned int delegateVideoPlayerNodeDidStartInitialLoading:1;
unsigned int delegateVideoPlayerNodeDidFinishInitialLoading:1;
unsigned int delegateVideoPlayerNodeDidRecoverFromStall:1;
} _delegateFlags; } _delegateFlags;
NSURL *_url; NSURL *_url;
AVAsset *_asset; AVAsset *_asset;
AVVideoComposition *_videoComposition;
AVAudioMix *_audioMix;
ASVideoNode *_videoNode; ASVideoNode *_videoNode;
@@ -118,6 +125,22 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
return self; return self;
} }
-(instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix
{
if (!(self = [super init])) {
return nil;
}
_asset = asset;
_videoComposition = videoComposition;
_audioMix = audioMix;
_loadAssetWhenNodeBecomesVisible = YES;
[self _init];
return self;
}
- (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible - (instancetype)initWithUrl:(NSURL *)url loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
{ {
if (!(self = [super init])) { if (!(self = [super init])) {
@@ -147,6 +170,22 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
return self; return self;
} }
-(instancetype)initWithAsset:(AVAsset *)asset videoComposition:(AVVideoComposition *)videoComposition audioMix:(AVAudioMix *)audioMix loadAssetWhenNodeBecomesVisible:(BOOL)loadAssetWhenNodeBecomesVisible
{
if (!(self = [super init])) {
return nil;
}
_asset = asset;
_videoComposition = videoComposition;
_audioMix = audioMix;
_loadAssetWhenNodeBecomesVisible = loadAssetWhenNodeBecomesVisible;
[self _init];
return self;
}
- (void)_init - (void)_init
{ {
_defaultControlsColor = [UIColor whiteColor]; _defaultControlsColor = [UIColor whiteColor];
@@ -156,6 +195,8 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
_videoNode.delegate = self; _videoNode.delegate = self;
if (_loadAssetWhenNodeBecomesVisible == NO) { if (_loadAssetWhenNodeBecomesVisible == NO) {
_videoNode.asset = _asset; _videoNode.asset = _asset;
_videoNode.videoComposition = _videoComposition;
_videoNode.audioMix = _audioMix;
} }
[self addSubnode:_videoNode]; [self addSubnode:_videoNode];
} }
@@ -175,9 +216,17 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);
if (isVisible && _loadAssetWhenNodeBecomesVisible && _asset != _videoNode.asset) { if (isVisible && _loadAssetWhenNodeBecomesVisible) {
if (_asset != _videoNode.asset) {
_videoNode.asset = _asset; _videoNode.asset = _asset;
} }
if (_videoComposition != _videoNode.videoComposition) {
_videoNode.videoComposition = _videoComposition;
}
if (_audioMix != _videoNode.audioMix) {
_videoNode.audioMix = _audioMix;
}
}
} }
- (NSArray *)createDefaultControlElementArray - (NSArray *)createDefaultControlElementArray
@@ -480,6 +529,41 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
} }
} }
- (void)videoNode:(ASVideoNode *)videoNode didSetCurrentItem:(AVPlayerItem *)currentItem
{
if (_delegateFlags.delegateVideoPlayerNodeDidSetCurrentItem) {
[_delegate videoPlayerNode:self didSetCurrentItem:currentItem];
}
}
- (void)videoNode:(ASVideoNode *)videoNode didStallAtTimeInterval:(NSTimeInterval)timeInterval
{
if (_delegateFlags.delegateVideoPlayerNodeDidStallAtTimeInterval) {
[_delegate videoPlayerNode:self didStallAtTimeInterval:timeInterval];
}
}
- (void)videoNodeDidStartInitialLoading:(ASVideoNode *)videoNode
{
if (_delegateFlags.delegateVideoPlayerNodeDidStartInitialLoading) {
[_delegate videoPlayerNodeDidStartInitialLoading:self];
}
}
- (void)videoNodeDidFinishInitialLoading:(ASVideoNode *)videoNode
{
if (_delegateFlags.delegateVideoPlayerNodeDidFinishInitialLoading) {
[_delegate videoPlayerNodeDidFinishInitialLoading:self];
}
}
- (void)videoNodeDidRecoverFromStall:(ASVideoNode *)videoNode
{
if (_delegateFlags.delegateVideoPlayerNodeDidRecoverFromStall) {
[_delegate videoPlayerNodeDidRecoverFromStall:self];
}
}
#pragma mark - Actions #pragma mark - Actions
- (void)togglePlayPause - (void)togglePlayPause
{ {
@@ -702,6 +786,11 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
_delegateFlags.delegateTimeLabelAttributedString = [_delegate respondsToSelector:@selector(videoPlayerNode:timeStringForTimeLabelType:forTime:)]; _delegateFlags.delegateTimeLabelAttributedString = [_delegate respondsToSelector:@selector(videoPlayerNode:timeStringForTimeLabelType:forTime:)];
_delegateFlags.delegatePlaybackButtonTint = [_delegate respondsToSelector:@selector(videoPlayerNodePlaybackButtonTint:)]; _delegateFlags.delegatePlaybackButtonTint = [_delegate respondsToSelector:@selector(videoPlayerNodePlaybackButtonTint:)];
_delegateFlags.delegateDidTapVideoPlayerNode = [_delegate respondsToSelector:@selector(didTapVideoPlayerNode:)]; _delegateFlags.delegateDidTapVideoPlayerNode = [_delegate respondsToSelector:@selector(didTapVideoPlayerNode:)];
_delegateFlags.delegateVideoPlayerNodeDidSetCurrentItem = [_delegate respondsToSelector:@selector(videoPlayerNode:didSetCurrentItem:)];
_delegateFlags.delegateVideoPlayerNodeDidStallAtTimeInterval = [_delegate respondsToSelector:@selector(videoPlayerNode:didStallAtTimeInterval:)];
_delegateFlags.delegateVideoPlayerNodeDidStartInitialLoading = [_delegate respondsToSelector:@selector(videoPlayerNodeDidStartInitialLoading:)];
_delegateFlags.delegateVideoPlayerNodeDidFinishInitialLoading = [_delegate respondsToSelector:@selector(videoPlayerNodeDidFinishInitialLoading:)];
_delegateFlags.delegateVideoPlayerNodeDidRecoverFromStall = [_delegate respondsToSelector:@selector(videoPlayerNodeDidRecoverFromStall:)];
} }
} }