From 4060f40d324e0cbad01d05b8d69eb7efa601d04e Mon Sep 17 00:00:00 2001 From: Gareth Reese Date: Wed, 2 Mar 2016 13:53:19 +0000 Subject: [PATCH] Added preview images for single file URLs. Unfortunately doesn't seem possible for HLS video. --- AsyncDisplayKit/ASVideoNode.mm | 85 ++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/AsyncDisplayKit/ASVideoNode.mm b/AsyncDisplayKit/ASVideoNode.mm index 3f41c06281..c09bf6f8c4 100644 --- a/AsyncDisplayKit/ASVideoNode.mm +++ b/AsyncDisplayKit/ASVideoNode.mm @@ -42,13 +42,15 @@ @implementation ASVideoNode -//TODO: Have a bash at getting the preview images sorted for the URL types - might need to observe until it's loaded - //TODO: Have a bash at supplying a preview image node so that we're deferring the construction of the video as it eats memory at the moment // [[[[playerItem tracks] objectAtIndex:0] assetTrack] asset] //TODO: URL file videos don't seem to repeat +//TODO: Have a look at any unit tests + +//TODO: The preview image doesn't seem to scale with the video layout when you click on the item + #pragma mark - Construction and Layout - (instancetype)initWithURL:(NSURL*)url @@ -99,7 +101,8 @@ ASDisplayNode* playerNode = [[ASDisplayNode alloc] initWithLayerBlock:^CALayer *{ AVPlayerLayer *playerLayer = [[AVPlayerLayer alloc] init]; if (!_player) { - _player = [AVPlayer playerWithPlayerItem:[self constructPlayerItemFromInitData]]; + _currentItem = [self constructPlayerItemFromInitData]; + _player = [AVPlayer playerWithPlayerItem:_currentItem]; _player.muted = _muted; } playerLayer.player = _player; @@ -131,32 +134,7 @@ _playerNode = [self constructPlayerNode]; [self insertSubnode:_playerNode atIndex:0]; } else if (_asset) { - dispatch_async(_previewQueue, ^{ - AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:_asset]; - imageGenerator.appliesPreferredTrackTransform = YES; - [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; - - if ([_gravity isEqualToString:AVLayerVideoGravityResize]) { - _placeholderImageNode.contentMode = UIViewContentModeRedraw; - } - if ([_gravity isEqualToString:AVLayerVideoGravityResizeAspect]) { - _placeholderImageNode.contentMode = UIViewContentModeScaleAspectFit; - } - if ([_gravity isEqual:AVLayerVideoGravityResizeAspectFill]) { - _placeholderImageNode.contentMode = UIViewContentModeScaleAspectFill; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - _placeholderImageNode.frame = self.bounds; - [self insertSubnode:_placeholderImageNode atIndex:0]; - }); - }]; - }); + [self setPlaceholderImagefromAsset:_asset]; } } @@ -180,6 +158,41 @@ _spinner.position = CGPointMake(bounds.size.width/2, bounds.size.height/2); } +- (void)setPlaceholderImagefromAsset:(AVAsset*)asset { + dispatch_async(_previewQueue, ^{ + AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:_asset]; + imageGenerator.appliesPreferredTrackTransform = YES; + [imageGenerator generateCGImagesAsynchronouslyForTimes:@[[NSValue valueWithCMTime:CMTimeMake(0, 1)]] 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) { + UIImage *theImage = [UIImage imageWithCGImage:image]; + + _placeholderImageNode = [[ASImageNode alloc] init]; + _placeholderImageNode.layerBacked = YES; + _placeholderImageNode.image = theImage; + + if ([_gravity isEqualToString:AVLayerVideoGravityResize]) { + _placeholderImageNode.contentMode = UIViewContentModeRedraw; + } + if ([_gravity isEqualToString:AVLayerVideoGravityResizeAspect]) { + _placeholderImageNode.contentMode = UIViewContentModeScaleAspectFit; + } + if ([_gravity isEqual:AVLayerVideoGravityResizeAspectFill]) { + _placeholderImageNode.contentMode = UIViewContentModeScaleAspectFill; + } + + dispatch_async(dispatch_get_main_queue(), ^{ + _placeholderImageNode.frame = self.bounds; + [self insertSubnode:_placeholderImageNode atIndex:0]; + [self setNeedsLayout]; + }); + } + }]; + }); +} + - (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState { if (!(newState & ASInterfaceStateVisible)) { @@ -205,6 +218,17 @@ [_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 (_currentItem && + _currentItem.tracks.count > 0 && + _currentItem.tracks[0].assetTrack && + _currentItem.tracks[0].assetTrack.asset) { + _asset = _currentItem.tracks[0].assetTrack.asset; + [self setPlaceholderImagefromAsset:_asset]; + } + } } if ([[change objectForKey:@"new"] integerValue] == AVPlayerItemStatusFailed) { @@ -289,7 +313,8 @@ if (isVisible) { if (_playerNode.isNodeLoaded) { if (!_player) { - _player = [AVPlayer playerWithPlayerItem:[self constructPlayerItemFromInitData]]; + _currentItem = [self constructPlayerItemFromInitData]; + _player = [AVPlayer playerWithPlayerItem:_currentItem]; _player.muted = _muted; } ((AVPlayerLayer *)_playerNode.layer).player = _player;