diff --git a/AsyncDisplayKit/ASVideoNode.mm b/AsyncDisplayKit/ASVideoNode.mm index 42d9d5ce36..af612f02ed 100644 --- a/AsyncDisplayKit/ASVideoNode.mm +++ b/AsyncDisplayKit/ASVideoNode.mm @@ -19,10 +19,13 @@ AVPlayerItem *_currentItem; AVPlayer *_player; + ASImageNode *_placeholderImageNode; + ASButtonNode *_playButton; ASDisplayNode *_playerNode; ASDisplayNode *_spinner; NSString *_gravity; + dispatch_queue_t _previewQueue; } @end @@ -33,6 +36,8 @@ { if (!(self = [super init])) { return nil; } + _previewQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + [ASDisplayNode setShouldUseNewRenderingRange:YES]; self.gravity = AVLayerVideoGravityResizeAspect; @@ -49,8 +54,6 @@ if (_shouldBePlaying) { [self pause]; _shouldBePlaying = YES; - } else { - [self pause]; } [(UIActivityIndicatorView *)_spinner.view stopAnimating]; [_spinner removeFromSupernode]; @@ -97,6 +100,8 @@ [super layout]; CGRect bounds = self.bounds; + + _placeholderImageNode.frame = bounds; _playerNode.frame = bounds; _playerNode.layer.frame = bounds; @@ -112,18 +117,37 @@ { [super didLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; - - _playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{ - AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init]; - if (!_player) { - _player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc] initWithAsset:_asset]]; - } - playerLayer.player = _player; - playerLayer.videoGravity = [self gravity]; - return playerLayer; - }]; - - [self insertSubnode:_playerNode atIndex:0]; + + if (_shouldBePlaying) { + _playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{ + AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init]; + if (!_player) { + _player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc] initWithAsset:_asset]]; + } + playerLayer.player = _player; + playerLayer.videoGravity = [self gravity]; + return playerLayer; + }]; + + [self insertSubnode:_playerNode atIndex:0]; + } else { + dispatch_async(_previewQueue, ^{ + AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:_asset]; + [imageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:CMTimeMake(0, 1)]] completionHandler:^(CMTime requestedTime, CGImageRef _Nullable image, CMTime actualTime, AVAssetImageGeneratorResult result, NSError * _Nullable error) { + UIImage *theImage = [UIImage imageWithCGImage:image]; + + _placeholderImageNode = [[ASImageNode alloc] init]; + _placeholderImageNode.layerBacked = YES; + _placeholderImageNode.image = theImage; + _placeholderImageNode.contentMode = UIViewContentModeScaleAspectFit; + + dispatch_async(dispatch_get_main_queue(), ^{ + _placeholderImageNode.frame = self.bounds; + [self insertSubnode:_placeholderImageNode atIndex:0]; + }); + }]; + }); + } } - (void)tapped @@ -277,6 +301,20 @@ }]; } + if (!_playerNode) { + _playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{ + AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init]; + if (!_player) { + _player = [AVPlayer playerWithPlayerItem:[[AVPlayerItem alloc] initWithAsset:_asset]]; + } + playerLayer.player = _player; + playerLayer.videoGravity = [self gravity]; + return playerLayer; + }]; + + [self addSubnode:_playerNode]; + } + [_player play]; _shouldBePlaying = YES; _playButton.alpha = 0.0; @@ -355,4 +393,3 @@ } @end - diff --git a/AsyncDisplayKitTests/ASVideoNodeTests.m b/AsyncDisplayKitTests/ASVideoNodeTests.m index 9b7875560b..50b06c1aee 100644 --- a/AsyncDisplayKitTests/ASVideoNodeTests.m +++ b/AsyncDisplayKitTests/ASVideoNodeTests.m @@ -127,15 +127,28 @@ XCTAssertNotNil(_videoNode.player); } -- (void)testPlayerLayerNodeIsAddedOnDidLoad +- (void)testPlayerLayerNodeIsAddedOnDidLoadIfVisibleAndAutoPlaying { _videoNode.asset = _firstAsset; + [_videoNode setInterfaceState:ASInterfaceStateNone]; [_videoNode didLoad]; - XCTAssert([_videoNode.subnodes containsObject:_videoNode.playerNode]); + XCTAssert(![_videoNode.subnodes containsObject:_videoNode.playerNode]); } +- (void)testPlayerLayerNodeIsNotAddedIfVisibleButShouldNotBePlaying +{ + _videoNode.asset = _firstAsset; + + [_videoNode pause]; + [_videoNode setInterfaceState:ASInterfaceStateVisible]; + [_videoNode didLoad]; + + XCTAssert(![_videoNode.subnodes containsObject:_videoNode.playerNode]); +} + + - (void)testVideoStartsPlayingOnDidDidBecomeVisibleWhenShouldAutoplay { _videoNode.asset = _firstAsset;