mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-12 17:30:34 +00:00
Merge pull request #1323 from gazreese/AVPlayerItem-construction
[ASVideoNode] Add support for HTTP Live Streaming Video
This commit is contained in:
commit
94d0d908dc
@ -18,7 +18,10 @@
|
|||||||
// in an issue on GitHub: https://github.com/facebook/AsyncDisplayKit/issues
|
// in an issue on GitHub: https://github.com/facebook/AsyncDisplayKit/issues
|
||||||
|
|
||||||
@interface ASVideoNode : ASControlNode
|
@interface ASVideoNode : ASControlNode
|
||||||
|
|
||||||
|
@property (atomic, strong, readwrite) NSURL *url;
|
||||||
@property (atomic, strong, readwrite) AVAsset *asset;
|
@property (atomic, strong, readwrite) AVAsset *asset;
|
||||||
|
|
||||||
@property (atomic, strong, readonly) AVPlayer *player;
|
@property (atomic, strong, readonly) AVPlayer *player;
|
||||||
@property (atomic, strong, readonly) AVPlayerItem *currentItem;
|
@property (atomic, strong, readonly) AVPlayerItem *currentItem;
|
||||||
|
|
||||||
@ -34,6 +37,8 @@
|
|||||||
|
|
||||||
@property (atomic, weak, readwrite) id<ASVideoNodeDelegate> delegate;
|
@property (atomic, weak, readwrite) id<ASVideoNodeDelegate> delegate;
|
||||||
|
|
||||||
|
- (instancetype)init;
|
||||||
|
|
||||||
- (void)play;
|
- (void)play;
|
||||||
- (void)pause;
|
- (void)pause;
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,9 @@
|
|||||||
BOOL _muted;
|
BOOL _muted;
|
||||||
|
|
||||||
AVAsset *_asset;
|
AVAsset *_asset;
|
||||||
|
NSURL *_url;
|
||||||
|
|
||||||
AVPlayerItem *_currentItem;
|
AVPlayerItem *_currentPlayerItem;
|
||||||
AVPlayer *_player;
|
AVPlayer *_player;
|
||||||
|
|
||||||
ASImageNode *_placeholderImageNode;
|
ASImageNode *_placeholderImageNode;
|
||||||
@ -41,70 +42,81 @@
|
|||||||
|
|
||||||
@implementation ASVideoNode
|
@implementation ASVideoNode
|
||||||
|
|
||||||
|
//TODO: Have a bash at supplying a preview image node for use with HLS videos as we can't have a priview with those
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Construction and Layout
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if (!(self = [super init])) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
_previewQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
|
_previewQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
|
||||||
|
|
||||||
self.playButton = [[ASDefaultPlayButton alloc] init];
|
self.playButton = [[ASDefaultPlayButton alloc] init];
|
||||||
|
|
||||||
self.gravity = AVLayerVideoGravityResizeAspect;
|
self.gravity = AVLayerVideoGravityResizeAspect;
|
||||||
|
|
||||||
[self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
[self addTarget:self action:@selector(tapped) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
|
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
||||||
{
|
{
|
||||||
[super interfaceStateDidChange:newState fromState:oldState];
|
ASDisplayNodeAssertNotSupported();
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (ASDisplayNode*)constructPlayerNode
|
||||||
|
{
|
||||||
|
ASDisplayNode* playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
|
||||||
|
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
|
||||||
|
if (!_player) {
|
||||||
|
[self constructCurrentPlayerItemFromInitData];
|
||||||
|
_player = [AVPlayer playerWithPlayerItem:_currentPlayerItem];
|
||||||
|
_player.muted = _muted;
|
||||||
|
}
|
||||||
|
playerLayer.player = _player;
|
||||||
|
playerLayer.videoGravity = [self gravity];
|
||||||
|
return playerLayer;
|
||||||
|
}];
|
||||||
|
|
||||||
|
return playerNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)constructCurrentPlayerItemFromInitData
|
||||||
|
{
|
||||||
|
ASDisplayNodeAssert(_asset || _url, @"ASVideoNode must be initialised with either an AVAsset or URL");
|
||||||
|
[self removePlayerItemObservers];
|
||||||
|
|
||||||
|
if (_asset) {
|
||||||
|
_currentPlayerItem = [[AVPlayerItem alloc] initWithAsset:_asset];
|
||||||
|
} else if (_url) {
|
||||||
|
_currentPlayerItem = [[AVPlayerItem alloc] initWithURL:_url];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentPlayerItem) {
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:_currentPlayerItem];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(errorWhilePlaying:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:_currentPlayerItem];
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(errorWhilePlaying:) name:AVPlayerItemNewErrorLogEntryNotification object:_currentPlayerItem];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removePlayerItemObservers
|
||||||
|
{
|
||||||
|
if (_currentPlayerItem) {
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemFailedToPlayToEndTimeNotification object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemNewErrorLogEntryNotification object:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didLoad
|
||||||
|
{
|
||||||
|
[super didLoad];
|
||||||
|
|
||||||
if (!(newState & ASInterfaceStateVisible)) {
|
|
||||||
if (oldState & ASInterfaceStateVisible) {
|
|
||||||
if (_shouldBePlaying) {
|
if (_shouldBePlaying) {
|
||||||
[self pause];
|
_playerNode = [self constructPlayerNode];
|
||||||
_shouldBePlaying = YES;
|
[self insertSubnode:_playerNode atIndex:0];
|
||||||
}
|
} else if (_asset) {
|
||||||
[(UIActivityIndicatorView *)_spinner.view stopAnimating];
|
[self setPlaceholderImagefromAsset:_asset];
|
||||||
[_spinner removeFromSupernode];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_shouldBePlaying) {
|
|
||||||
[self play];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
|
||||||
{
|
|
||||||
if ([change[@"new"] integerValue] == AVPlayerItemStatusReadyToPlay) {
|
|
||||||
if ([self.subnodes containsObject:_spinner]) {
|
|
||||||
[_spinner removeFromSupernode];
|
|
||||||
_spinner = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([change[@"new"] integerValue] == AVPlayerItemStatusFailed) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)didPlayToEnd:(NSNotification *)notification
|
|
||||||
{
|
|
||||||
if (ASObjectIsEqual([[notification object] asset], _asset)) {
|
|
||||||
if ([_delegate respondsToSelector:@selector(videoPlaybackDidFinish:)]) {
|
|
||||||
[_delegate videoPlaybackDidFinish:self];
|
|
||||||
}
|
|
||||||
[_player seekToTime:CMTimeMakeWithSeconds(0, 1)];
|
|
||||||
|
|
||||||
if (_shouldAutorepeat) {
|
|
||||||
[self play];
|
|
||||||
} else {
|
|
||||||
[self pause];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,29 +140,22 @@
|
|||||||
_spinner.position = CGPointMake(bounds.size.width/2, bounds.size.height/2);
|
_spinner.position = CGPointMake(bounds.size.width/2, bounds.size.height/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didLoad
|
- (void)setPlaceholderImagefromAsset:(AVAsset*)asset
|
||||||
{
|
{
|
||||||
[super didLoad];
|
ASDN::MutexLocker l(_videoLock);
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
|
if (!_placeholderImageNode)
|
||||||
|
_placeholderImageNode = [[ASImageNode alloc] init];
|
||||||
|
|
||||||
if (_shouldBePlaying) {
|
|
||||||
_playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
|
|
||||||
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
|
|
||||||
if (!_player) {
|
|
||||||
_player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc] initWithAsset:_asset]];
|
|
||||||
_player.muted = _muted;
|
|
||||||
}
|
|
||||||
playerLayer.player = _player;
|
|
||||||
playerLayer.videoGravity = [self gravity];
|
|
||||||
return playerLayer;
|
|
||||||
}];
|
|
||||||
|
|
||||||
[self insertSubnode:_playerNode atIndex:0];
|
|
||||||
} else {
|
|
||||||
dispatch_async(_previewQueue, ^{
|
dispatch_async(_previewQueue, ^{
|
||||||
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:_asset];
|
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:_asset];
|
||||||
imageGenerator.appliesPreferredTrackTransform = YES;
|
imageGenerator.appliesPreferredTrackTransform = YES;
|
||||||
[imageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:CMTimeMake(0, 1)]] completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
|
NSArray *times = @[[NSValue valueWithCMTime:CMTimeMake(0, 1)]];
|
||||||
|
|
||||||
|
[imageGenerator generateCGImagesAsynchronouslyForTimes:times completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) {
|
||||||
|
|
||||||
|
// Unfortunately it's not possible to generate a preview image for an HTTP live stream asset, so we'll give up here
|
||||||
|
// http://stackoverflow.com/questions/32112205/m3u8-file-avassetimagegenerator-error
|
||||||
|
if (image && _placeholderImageNode.image == nil) {
|
||||||
UIImage *theImage = [UIImage imageWithCGImage:image];
|
UIImage *theImage = [UIImage imageWithCGImage:image];
|
||||||
|
|
||||||
_placeholderImageNode = [[ASImageNode alloc] init];
|
_placeholderImageNode = [[ASImageNode alloc] init];
|
||||||
@ -171,9 +176,59 @@
|
|||||||
_placeholderImageNode.frame = self.bounds;
|
_placeholderImageNode.frame = self.bounds;
|
||||||
[self insertSubnode:_placeholderImageNode atIndex:0];
|
[self insertSubnode:_placeholderImageNode atIndex:0];
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
|
||||||
|
{
|
||||||
|
[super interfaceStateDidChange:newState fromState:oldState];
|
||||||
|
|
||||||
|
BOOL nowVisible = ASInterfaceStateIncludesVisible(newState);
|
||||||
|
BOOL wasVisible = ASInterfaceStateIncludesVisible(oldState);
|
||||||
|
|
||||||
|
if (!nowVisible) {
|
||||||
|
if (wasVisible) {
|
||||||
|
if (_shouldBePlaying) {
|
||||||
|
[self pause];
|
||||||
|
_shouldBePlaying = YES;
|
||||||
|
}
|
||||||
|
[(UIActivityIndicatorView *)_spinner.view stopAnimating];
|
||||||
|
[_spinner removeFromSupernode];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_shouldBePlaying) {
|
||||||
|
[self play];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||||
|
{
|
||||||
|
if (object == _currentPlayerItem && [keyPath isEqualToString:@"status"]) {
|
||||||
|
if (_currentPlayerItem.status == AVPlayerItemStatusReadyToPlay) {
|
||||||
|
if ([self.subnodes containsObject:_spinner]) {
|
||||||
|
[_spinner removeFromSupernode];
|
||||||
|
_spinner = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't yet have a placeholder image update it now that we should have data available for it
|
||||||
|
if (!_placeholderImageNode) {
|
||||||
|
if (_currentPlayerItem &&
|
||||||
|
_currentPlayerItem.tracks.count > 0 &&
|
||||||
|
_currentPlayerItem.tracks[0].assetTrack &&
|
||||||
|
_currentPlayerItem.tracks[0].assetTrack.asset) {
|
||||||
|
_asset = _currentPlayerItem.tracks[0].assetTrack.asset;
|
||||||
|
[self setPlaceholderImagefromAsset:_asset];
|
||||||
|
[self setNeedsLayout];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (_currentPlayerItem.status == AVPlayerItemStatusFailed) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)tapped
|
- (void)tapped
|
||||||
@ -189,18 +244,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssertNotSupported();
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)fetchData
|
- (void)fetchData
|
||||||
{
|
{
|
||||||
[super fetchData];
|
[super fetchData];
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
[_currentItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(status))];
|
[_currentPlayerItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(status))];
|
||||||
}
|
}
|
||||||
@catch (NSException * __unused exception) {
|
@catch (NSException * __unused exception) {
|
||||||
NSLog(@"unnecessary removal in fetch data");
|
NSLog(@"unnecessary removal in fetch data");
|
||||||
@ -208,13 +257,13 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_videoLock);
|
ASDN::MutexLocker l(_videoLock);
|
||||||
_currentItem = [[AVPlayerItem alloc] initWithAsset:_asset];
|
[self constructCurrentPlayerItemFromInitData];
|
||||||
[_currentItem addObserver:self forKeyPath:NSStringFromSelector(@selector(status)) options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL];
|
[_currentPlayerItem addObserver:self forKeyPath:NSStringFromSelector(@selector(status)) options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:NULL];
|
||||||
|
|
||||||
if (_player) {
|
if (_player) {
|
||||||
[_player replaceCurrentItemWithPlayerItem:_currentItem];
|
[_player replaceCurrentItemWithPlayerItem:_currentPlayerItem];
|
||||||
} else {
|
} else {
|
||||||
_player = [[AVPlayer alloc] initWithPlayerItem:_currentItem];
|
_player = [[AVPlayer alloc] initWithPlayerItem:_currentPlayerItem];
|
||||||
_player.muted = _muted;
|
_player.muted = _muted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,7 +294,8 @@
|
|||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
if (_playerNode.isNodeLoaded) {
|
if (_playerNode.isNodeLoaded) {
|
||||||
if (!_player) {
|
if (!_player) {
|
||||||
_player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc] initWithAsset:_asset]];
|
[self constructCurrentPlayerItemFromInitData];
|
||||||
|
_player = [AVPlayer playerWithPlayerItem:_currentPlayerItem];
|
||||||
_player.muted = _muted;
|
_player.muted = _muted;
|
||||||
}
|
}
|
||||||
((AVPlayerLayer *)_playerNode.layer).player = _player;
|
((AVPlayerLayer *)_playerNode.layer).player = _player;
|
||||||
@ -257,6 +307,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Video Properties
|
#pragma mark - Video Properties
|
||||||
|
|
||||||
- (void)setPlayButton:(ASButtonNode *)playButton
|
- (void)setPlayButton:(ASButtonNode *)playButton
|
||||||
@ -302,6 +353,27 @@
|
|||||||
return _asset;
|
return _asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setUrl:(NSURL *)url
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_videoLock);
|
||||||
|
|
||||||
|
if (ASObjectIsEqual(url, _url))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_url = url;
|
||||||
|
|
||||||
|
// FIXME: Adopt -setNeedsFetchData when it is available
|
||||||
|
if (self.interfaceState & ASInterfaceStateFetchData) {
|
||||||
|
[self fetchData];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSURL *)url
|
||||||
|
{
|
||||||
|
ASDN::MutexLocker l(_videoLock);
|
||||||
|
return _url;
|
||||||
|
}
|
||||||
|
|
||||||
- (AVPlayer *)player
|
- (AVPlayer *)player
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_videoLock);
|
ASDN::MutexLocker l(_videoLock);
|
||||||
@ -355,16 +427,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!_playerNode) {
|
if (!_playerNode) {
|
||||||
_playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
|
_playerNode = [self constructPlayerNode];
|
||||||
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
|
|
||||||
if (!_player) {
|
|
||||||
_player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc] initWithAsset:_asset]];
|
|
||||||
_player.muted = _muted;
|
|
||||||
}
|
|
||||||
playerLayer.player = _player;
|
|
||||||
playerLayer.videoGravity = [self gravity];
|
|
||||||
return playerLayer;
|
|
||||||
}];
|
|
||||||
|
|
||||||
if ([self.subnodes containsObject:_playButton]) {
|
if ([self.subnodes containsObject:_playButton]) {
|
||||||
[self insertSubnode:_playerNode belowSubnode:_playButton];
|
[self insertSubnode:_playerNode belowSubnode:_playButton];
|
||||||
@ -380,7 +443,7 @@
|
|||||||
_playButton.alpha = 0.0;
|
_playButton.alpha = 0.0;
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (![self ready] && _shouldBePlaying && (self.interfaceState & ASInterfaceStateVisible)) {
|
if (![self ready] && _shouldBePlaying && ASInterfaceStateIncludesVisible(self.interfaceState)) {
|
||||||
[self addSubnode:_spinner];
|
[self addSubnode:_spinner];
|
||||||
[(UIActivityIndicatorView *)_spinner.view startAnimating];
|
[(UIActivityIndicatorView *)_spinner.view startAnimating];
|
||||||
}
|
}
|
||||||
@ -388,7 +451,7 @@
|
|||||||
|
|
||||||
- (BOOL)ready
|
- (BOOL)ready
|
||||||
{
|
{
|
||||||
return _currentItem.status == AVPlayerItemStatusReadyToPlay;
|
return _currentPlayerItem.status == AVPlayerItemStatusReadyToPlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)pause
|
- (void)pause
|
||||||
@ -410,6 +473,41 @@
|
|||||||
return (_player.rate > 0 && !_player.error);
|
return (_player.rate > 0 && !_player.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Playback observers
|
||||||
|
|
||||||
|
- (void)didPlayToEnd:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
if ([_delegate respondsToSelector:@selector(videoPlaybackDidFinish:)]) {
|
||||||
|
[_delegate videoPlaybackDidFinish:self];
|
||||||
|
}
|
||||||
|
[_player seekToTime:CMTimeMakeWithSeconds(0, 1)];
|
||||||
|
|
||||||
|
if (_shouldAutorepeat) {
|
||||||
|
[self play];
|
||||||
|
} else {
|
||||||
|
[self pause];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)errorWhilePlaying:(NSNotification *)notification
|
||||||
|
{
|
||||||
|
if ([notification.name isEqualToString:AVPlayerItemFailedToPlayToEndTimeNotification]) {
|
||||||
|
NSLog(@"Failed to play video");
|
||||||
|
}
|
||||||
|
else if ([notification.name isEqualToString:AVPlayerItemNewErrorLogEntryNotification]) {
|
||||||
|
AVPlayerItem* item = (AVPlayerItem*)notification.object;
|
||||||
|
AVPlayerItemErrorLogEvent* logEvent = item.errorLog.events.lastObject;
|
||||||
|
NSLog(@"AVPlayerItem error log entry added for video with error %@ status %@", item.error,
|
||||||
|
(item.status == AVPlayerItemStatusFailed ? @"FAILED" : [NSString stringWithFormat:@"%ld", (long)item.status]));
|
||||||
|
NSLog(@"Item is %@", item);
|
||||||
|
|
||||||
|
if (logEvent)
|
||||||
|
NSLog(@"Log code %ld domain %@ comment %@", (long)logEvent.errorStatusCode, logEvent.errorDomain, logEvent.errorComment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Property Accessors for Tests
|
#pragma mark - Property Accessors for Tests
|
||||||
|
|
||||||
- (ASDisplayNode *)spinner
|
- (ASDisplayNode *)spinner
|
||||||
@ -421,13 +519,13 @@
|
|||||||
- (AVPlayerItem *)currentItem
|
- (AVPlayerItem *)currentItem
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_videoLock);
|
ASDN::MutexLocker l(_videoLock);
|
||||||
return _currentItem;
|
return _currentPlayerItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setCurrentItem:(AVPlayerItem *)currentItem
|
- (void)setCurrentItem:(AVPlayerItem *)currentItem
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_videoLock);
|
ASDN::MutexLocker l(_videoLock);
|
||||||
_currentItem = currentItem;
|
_currentPlayerItem = currentItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (ASDisplayNode *)playerNode
|
- (ASDisplayNode *)playerNode
|
||||||
@ -446,9 +544,10 @@
|
|||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
|
[self removePlayerItemObservers];
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
[_currentItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(status))];
|
[_currentPlayerItem removeObserver:self forKeyPath:NSStringFromSelector(@selector(status))];
|
||||||
}
|
}
|
||||||
@catch (NSException * __unused exception) {
|
@catch (NSException * __unused exception) {
|
||||||
NSLog(@"unnecessary removal in dealloc");
|
NSLog(@"unnecessary removal in dealloc");
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
ASVideoNode *_videoNode;
|
ASVideoNode *_videoNode;
|
||||||
AVURLAsset *_firstAsset;
|
AVURLAsset *_firstAsset;
|
||||||
AVAsset *_secondAsset;
|
AVAsset *_secondAsset;
|
||||||
|
NSURL *_url;
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -52,65 +53,75 @@
|
|||||||
_videoNode = [[ASVideoNode alloc] init];
|
_videoNode = [[ASVideoNode alloc] init];
|
||||||
_firstAsset = [AVURLAsset assetWithURL:[NSURL URLWithString:@"firstURL"]];
|
_firstAsset = [AVURLAsset assetWithURL:[NSURL URLWithString:@"firstURL"]];
|
||||||
_secondAsset = [AVAsset assetWithURL:[NSURL URLWithString:@"secondURL"]];
|
_secondAsset = [AVAsset assetWithURL:[NSURL URLWithString:@"secondURL"]];
|
||||||
|
_url = [NSURL URLWithString:@"testURL"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testVideoNodeReplacesAVPlayerItemWhenNewURLIsSet
|
|
||||||
{
|
|
||||||
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
|
||||||
_videoNode.asset = _firstAsset;
|
|
||||||
|
|
||||||
AVPlayerItem *item = [_videoNode currentItem];
|
|
||||||
|
|
||||||
_videoNode.asset = _secondAsset;
|
|
||||||
AVPlayerItem *secondItem = [_videoNode currentItem];
|
|
||||||
|
|
||||||
XCTAssertNotEqualObjects(item, secondItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testVideoNodeDoesNotReplaceAVPlayerItemWhenSameURLIsSet
|
|
||||||
{
|
|
||||||
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
|
||||||
|
|
||||||
_videoNode.asset = _firstAsset;
|
|
||||||
AVPlayerItem *item = [_videoNode currentItem];
|
|
||||||
|
|
||||||
_videoNode.asset = [AVAsset assetWithURL:_firstAsset.URL];
|
|
||||||
AVPlayerItem *secondItem = [_videoNode currentItem];
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(item, secondItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testSpinnerDefaultsToNil
|
- (void)testSpinnerDefaultsToNil
|
||||||
{
|
{
|
||||||
XCTAssertNil(_videoNode.spinner);
|
XCTAssertNil(_videoNode.spinner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnode
|
- (void)testOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnode
|
||||||
{
|
{
|
||||||
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
[self doOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnodeWithUrl];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnodeWithUrl
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
[self doOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnodeWithUrl];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doOnPlayIfVideoIsNotReadyInitializeSpinnerAndAddAsSubnodeWithUrl
|
||||||
|
{
|
||||||
|
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
||||||
[_videoNode play];
|
[_videoNode play];
|
||||||
|
|
||||||
XCTAssertNotNil(_videoNode.spinner);
|
XCTAssertNotNil(_videoNode.spinner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testOnPauseSpinnerIsPausedIfPresent
|
- (void)testOnPauseSpinnerIsPausedIfPresent
|
||||||
{
|
{
|
||||||
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
[self doOnPauseSpinnerIsPausedIfPresentWithURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testOnPauseSpinnerIsPausedIfPresentWithURL
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
[self doOnPauseSpinnerIsPausedIfPresentWithURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doOnPauseSpinnerIsPausedIfPresentWithURL
|
||||||
|
{
|
||||||
|
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
||||||
|
|
||||||
[_videoNode play];
|
[_videoNode play];
|
||||||
|
|
||||||
[_videoNode pause];
|
[_videoNode pause];
|
||||||
|
|
||||||
XCTAssertFalse(((UIActivityIndicatorView *)_videoNode.spinner.view).isAnimating);
|
XCTAssertFalse(((UIActivityIndicatorView *)_videoNode.spinner.view).isAnimating);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testOnVideoReadySpinnerIsStoppedAndRemoved
|
- (void)testOnVideoReadySpinnerIsStoppedAndRemoved
|
||||||
{
|
{
|
||||||
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
[self doOnVideoReadySpinnerIsStoppedAndRemovedWithURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testOnVideoReadySpinnerIsStoppedAndRemovedWithURL
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
[self doOnVideoReadySpinnerIsStoppedAndRemovedWithURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doOnVideoReadySpinnerIsStoppedAndRemovedWithURL
|
||||||
|
{
|
||||||
|
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
||||||
|
|
||||||
[_videoNode play];
|
[_videoNode play];
|
||||||
[_videoNode observeValueForKeyPath:@"status" ofObject:[_videoNode currentItem] change:@{@"new" : @(AVPlayerItemStatusReadyToPlay)} context:NULL];
|
[_videoNode observeValueForKeyPath:@"status" ofObject:[_videoNode currentItem] change:@{@"new" : @(AVPlayerItemStatusReadyToPlay)} context:NULL];
|
||||||
@ -118,34 +129,71 @@
|
|||||||
XCTAssertFalse(((UIActivityIndicatorView *)_videoNode.spinner.view).isAnimating);
|
XCTAssertFalse(((UIActivityIndicatorView *)_videoNode.spinner.view).isAnimating);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testPlayerDefaultsToNil
|
- (void)testPlayerDefaultsToNil
|
||||||
{
|
{
|
||||||
|
_videoNode.asset = _firstAsset;
|
||||||
|
XCTAssertNil(_videoNode.player);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testPlayerDefaultsToNilWithURL
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
XCTAssertNil(_videoNode.player);
|
XCTAssertNil(_videoNode.player);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPlayerIsCreatedInFetchData
|
- (void)testPlayerIsCreatedInFetchData
|
||||||
{
|
{
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
|
||||||
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
||||||
|
|
||||||
XCTAssertNotNil(_videoNode.player);
|
XCTAssertNotNil(_videoNode.player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testPlayerIsCreatedInFetchDataWithURL
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
_videoNode.interfaceState = ASInterfaceStateFetchData;
|
||||||
|
|
||||||
|
XCTAssertNotNil(_videoNode.player);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testPlayerLayerNodeIsAddedOnDidLoadIfVisibleAndAutoPlaying
|
- (void)testPlayerLayerNodeIsAddedOnDidLoadIfVisibleAndAutoPlaying
|
||||||
{
|
{
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
[self doPlayerLayerNodeIsAddedOnDidLoadIfVisibleAndAutoPlayingWithURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testPlayerLayerNodeIsAddedOnDidLoadIfVisibleAndAutoPlayingWithURL
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
[self doPlayerLayerNodeIsAddedOnDidLoadIfVisibleAndAutoPlayingWithURL];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doPlayerLayerNodeIsAddedOnDidLoadIfVisibleAndAutoPlayingWithURL
|
||||||
|
{
|
||||||
[_videoNode setInterfaceState:ASInterfaceStateNone];
|
[_videoNode setInterfaceState:ASInterfaceStateNone];
|
||||||
[_videoNode didLoad];
|
[_videoNode didLoad];
|
||||||
|
|
||||||
XCTAssert(![_videoNode.subnodes containsObject:_videoNode.playerNode]);
|
XCTAssert(![_videoNode.subnodes containsObject:_videoNode.playerNode]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testPlayerLayerNodeIsNotAddedIfVisibleButShouldNotBePlaying
|
- (void)testPlayerLayerNodeIsNotAddedIfVisibleButShouldNotBePlaying
|
||||||
{
|
{
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
[self doPlayerLayerNodeIsNotAddedIfVisibleButShouldNotBePlaying];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testPlayerLayerNodeIsNotAddedIfVisibleButShouldNotBePlayingWithUrl
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
[self doPlayerLayerNodeIsNotAddedIfVisibleButShouldNotBePlaying];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doPlayerLayerNodeIsNotAddedIfVisibleButShouldNotBePlaying
|
||||||
|
{
|
||||||
[_videoNode pause];
|
[_videoNode pause];
|
||||||
[_videoNode setInterfaceState:ASInterfaceStateVisible | ASInterfaceStateDisplay];
|
[_videoNode setInterfaceState:ASInterfaceStateVisible | ASInterfaceStateDisplay];
|
||||||
[_videoNode didLoad];
|
[_videoNode didLoad];
|
||||||
@ -157,6 +205,17 @@
|
|||||||
- (void)testVideoStartsPlayingOnDidDidBecomeVisibleWhenShouldAutoplay
|
- (void)testVideoStartsPlayingOnDidDidBecomeVisibleWhenShouldAutoplay
|
||||||
{
|
{
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
[self doVideoStartsPlayingOnDidDidBecomeVisibleWhenShouldAutoplay];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testVideoStartsPlayingOnDidDidBecomeVisibleWhenShouldAutoplayWithURL
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
[self doVideoStartsPlayingOnDidDidBecomeVisibleWhenShouldAutoplay];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doVideoStartsPlayingOnDidDidBecomeVisibleWhenShouldAutoplay
|
||||||
|
{
|
||||||
_videoNode.shouldAutoplay = YES;
|
_videoNode.shouldAutoplay = YES;
|
||||||
_videoNode.playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
|
_videoNode.playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{
|
||||||
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
|
AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init];
|
||||||
@ -169,9 +228,21 @@
|
|||||||
XCTAssertTrue(_videoNode.shouldBePlaying);
|
XCTAssertTrue(_videoNode.shouldBePlaying);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testVideoShouldPauseWhenItLeavesVisibleButShouldKnowPlayingShouldRestartLater
|
- (void)testVideoShouldPauseWhenItLeavesVisibleButShouldKnowPlayingShouldRestartLater
|
||||||
{
|
{
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
[self doVideoShouldPauseWhenItLeavesVisibleButShouldKnowPlayingShouldRestartLater];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testVideoShouldPauseWhenItLeavesVisibleButShouldKnowPlayingShouldRestartLaterWithURL
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
[self doVideoShouldPauseWhenItLeavesVisibleButShouldKnowPlayingShouldRestartLater];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doVideoShouldPauseWhenItLeavesVisibleButShouldKnowPlayingShouldRestartLater
|
||||||
|
{
|
||||||
[_videoNode play];
|
[_videoNode play];
|
||||||
|
|
||||||
[_videoNode interfaceStateDidChange:ASInterfaceStateNone fromState:ASInterfaceStateVisible];
|
[_videoNode interfaceStateDidChange:ASInterfaceStateNone fromState:ASInterfaceStateVisible];
|
||||||
@ -180,9 +251,21 @@
|
|||||||
XCTAssertTrue(_videoNode.shouldBePlaying);
|
XCTAssertTrue(_videoNode.shouldBePlaying);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)testVideoThatIsPlayingWhenItLeavesVisibleRangeStartsAgainWhenItComesBack
|
- (void)testVideoThatIsPlayingWhenItLeavesVisibleRangeStartsAgainWhenItComesBack
|
||||||
{
|
{
|
||||||
_videoNode.asset = _firstAsset;
|
_videoNode.asset = _firstAsset;
|
||||||
|
[self doVideoThatIsPlayingWhenItLeavesVisibleRangeStartsAgainWhenItComesBack];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testVideoThatIsPlayingWhenItLeavesVisibleRangeStartsAgainWhenItComesBackWithURL
|
||||||
|
{
|
||||||
|
_videoNode.url = _url;
|
||||||
|
[self doVideoThatIsPlayingWhenItLeavesVisibleRangeStartsAgainWhenItComesBack];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)doVideoThatIsPlayingWhenItLeavesVisibleRangeStartsAgainWhenItComesBack
|
||||||
|
{
|
||||||
[_videoNode play];
|
[_videoNode play];
|
||||||
|
|
||||||
[_videoNode interfaceStateDidChange:ASInterfaceStateVisible fromState:ASInterfaceStateNone];
|
[_videoNode interfaceStateDidChange:ASInterfaceStateVisible fromState:ASInterfaceStateNone];
|
||||||
|
|||||||
@ -81,15 +81,42 @@ static const CGFloat kInnerPadding = 10.0f;
|
|||||||
|
|
||||||
_kittenSize = size;
|
_kittenSize = size;
|
||||||
|
|
||||||
|
u_int32_t videoInitMethod = arc4random_uniform(3);
|
||||||
|
u_int32_t autoPlay = arc4random_uniform(2);
|
||||||
|
NSArray* methodArray = @[@"AVAsset", @"File URL", @"HLS URL"];
|
||||||
|
NSArray* autoPlayArray = @[@"paused", @"auto play"];
|
||||||
|
|
||||||
|
switch (videoInitMethod) {
|
||||||
|
case 0:
|
||||||
|
// Construct an AVAsset from a URL
|
||||||
_videoNode = [[ASVideoNode alloc] init];
|
_videoNode = [[ASVideoNode alloc] init];
|
||||||
// _videoNode.shouldAutoplay = YES;
|
|
||||||
_videoNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
|
|
||||||
_videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
_videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// Construct the video node directly from the .mp4 URL
|
||||||
|
_videoNode = [[ASVideoNode alloc] init];
|
||||||
|
_videoNode.url = [NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
// Construct the video node from an HTTP Live Streaming URL
|
||||||
|
// URL from https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/02_Playback.html
|
||||||
|
_videoNode = [[ASVideoNode alloc] init];
|
||||||
|
_videoNode.url = [NSURL URLWithString:@"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autoPlay == 1)
|
||||||
|
_videoNode.shouldAutoplay = YES;
|
||||||
|
|
||||||
|
_videoNode.shouldAutorepeat = YES;
|
||||||
|
_videoNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
|
||||||
|
|
||||||
[self addSubnode:_videoNode];
|
[self addSubnode:_videoNode];
|
||||||
|
|
||||||
_textNode = [[ASTextNode alloc] init];
|
_textNode = [[ASTextNode alloc] init];
|
||||||
_textNode.attributedString = [[NSAttributedString alloc] initWithString:[self kittyIpsum]
|
_textNode.attributedString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ %@ %@", methodArray[videoInitMethod], autoPlayArray[autoPlay], [self kittyIpsum]]
|
||||||
attributes:[self textStyle]];
|
attributes:[self textStyle]];
|
||||||
[self addSubnode:_textNode];
|
[self addSubnode:_textNode];
|
||||||
|
|
||||||
|
|||||||
@ -33,9 +33,9 @@
|
|||||||
|
|
||||||
- (ASVideoNode *)guitarVideo;
|
- (ASVideoNode *)guitarVideo;
|
||||||
{
|
{
|
||||||
|
AVAsset* asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-3045b261-7e93-4492-b7e5-5d6358376c9f-editedLiveAndDie.mov"]];
|
||||||
ASVideoNode *videoNode = [[ASVideoNode alloc] init];
|
ASVideoNode *videoNode = [[ASVideoNode alloc] init];
|
||||||
|
videoNode.asset = asset;
|
||||||
videoNode.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-3045b261-7e93-4492-b7e5-5d6358376c9f-editedLiveAndDie.mov"]];
|
|
||||||
|
|
||||||
videoNode.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/3);
|
videoNode.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/3);
|
||||||
|
|
||||||
@ -48,12 +48,12 @@
|
|||||||
|
|
||||||
- (ASVideoNode *)nicCageVideo;
|
- (ASVideoNode *)nicCageVideo;
|
||||||
{
|
{
|
||||||
|
AVAsset* asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
||||||
ASVideoNode *nicCageVideo = [[ASVideoNode alloc] init];
|
ASVideoNode *nicCageVideo = [[ASVideoNode alloc] init];
|
||||||
|
nicCageVideo.asset = asset;
|
||||||
|
|
||||||
nicCageVideo.delegate = self;
|
nicCageVideo.delegate = self;
|
||||||
|
|
||||||
nicCageVideo.asset = [AVAsset assetWithURL:[NSURL URLWithString:@"https://files.parsetfss.com/8a8a3b0c-619e-4e4d-b1d5-1b5ba9bf2b42/tfss-753fe655-86bb-46da-89b7-aa59c60e49c0-niccage.mp4"]];
|
|
||||||
|
|
||||||
nicCageVideo.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3, [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
nicCageVideo.frame = CGRectMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3, [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||||
|
|
||||||
nicCageVideo.gravity = AVLayerVideoGravityResize;
|
nicCageVideo.gravity = AVLayerVideoGravityResize;
|
||||||
@ -68,10 +68,9 @@
|
|||||||
|
|
||||||
- (ASVideoNode *)simonVideo;
|
- (ASVideoNode *)simonVideo;
|
||||||
{
|
{
|
||||||
ASVideoNode *simonVideo = [[ASVideoNode alloc] init];
|
|
||||||
|
|
||||||
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"simon" ofType:@"mp4"]];
|
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"simon" ofType:@"mp4"]];
|
||||||
simonVideo.asset = [AVAsset assetWithURL:url];
|
ASVideoNode *simonVideo = [[ASVideoNode alloc] init];
|
||||||
|
simonVideo.url = url;
|
||||||
|
|
||||||
simonVideo.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height - ([UIScreen mainScreen].bounds.size.height/3), [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
simonVideo.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height - ([UIScreen mainScreen].bounds.size.height/3), [UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user