iOS: AVPlayer – Obtenir un instantané de l'image actuelle d'une video

J'ai passé toute la journée et ai traversé beaucoup de réponses SO, references Apple, documentations, etc, mais pas de succès.

Je veux une chose simple: je joue une video en utilisant AVPlayer et je veux mettre en pause et get le cadre actuel comme UIImage . C'est tout.

Ma video est un file m3u8 situé sur Internet, il est joué normalement dans AVPlayerLayer sans aucun problème.

Qu'ai-je essayé?

  1. AVAssetImageGenerator . Il ne fonctionne pas, la méthode copyCGImageAtTime:actualTime: error: renvoie null image réf. Selon la réponse , AVAssetImageGenerator ne fonctionne pas pour le streaming de videos.
  2. Prendre un instantané de la vue du joueur. J'ai d'abord essayé renderInContext: sur AVPlayerLayer , mais ensuite j'ai réalisé qu'il ne rend pas ce genre de couches "spéciales". Ensuite, j'ai trouvé une nouvelle méthode introduite dans iOS 7 – drawViewHierarchyInRect:afterScreenUpdates: qui devrait être capable de rendre aussi les couches spéciales, mais pas de chance, a toujours l'instantané de l'interface user avec une zone noire vide où la video est affichée.
  3. AVPlayerItemVideoOutput . J'ai ajouté une sortie video pour mon AVPlayerItem , cependant chaque fois que j'appelle hasNewPixelBufferForItemTime: il renvoie NO . Je suppose que le problème est encore en streaming video et je ne suis pas seul avec ce problème.
  4. AVAssetReader . Je pensais essayer mais j'ai décidé de ne pas perdre de time après avoir trouvé une question similaire ici .

Donc, n'y a-t-il pas moyen d'avoir un aperçu de quelque chose que je vois de toute façon à l'écran? Je ne peux pas le croire.

AVPlayerItemVideoOutput fonctionne bien pour moi à partir d'un m3u8. Peut-être que c'est parce que je ne consulte pas hasNewPixelBufferForItemTime et appelle simplement copyPixelBufferForItemTime ? Ce code produit un CVPixelBuffer au lieu d'un UIImage , mais il existe des réponses qui décrivent comment faire cela .

Cette réponse principalement cribbed à partir d' ici

 #import "ViewController.h" #import <AVFoundation/AVFoundation.h> @interface ViewController () @property (nonatomic) AVPlayer *player; @property (nonatomic) AVPlayerItem *playerItem; @property (nonatomic) AVPlayerItemVideoOutput *playerOutput; @end @implementation ViewController - (void)setupPlayerWithLoadedAsset:(AVAsset *)asset { NSDictionary* settings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) }; self.playerOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAtsortingbutes:settings]; self.playerItem = [AVPlayerItem playerItemWithAsset:asset]; [self.playerItem addOutput:self.playerOutput]; self.player = [AVPlayer playerWithPlayerItem:self.playerItem]; AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; playerLayer.frame = self.view.frame; [self.view.layer addSublayer:playerLayer]; [self.player play]; } - (IBAction)grabFrame { CVPixelBufferRef buffer = [self.playerOutput copyPixelBufferForItemTime:[self.playerItem currentTime] itemTimeForDisplay:nil]; NSLog(@"The image: %@", buffer); } - (void)viewDidLoad { [super viewDidLoad]; NSURL *someUrl = [NSURL URLWithSsortingng:@"http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"]; AVURLAsset *asset = [AVURLAsset URLAssetWithURL:someUrl options:nil]; [asset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracks"] completionHandler:^{ NSError* error = nil; AVKeyValueStatus status = [asset statusOfValueForKey:@"tracks" error:&error]; if (status == AVKeyValueStatusLoaded) { dispatch_async(dispatch_get_main_queue(), ^{ [self setupPlayerWithLoadedAsset:asset]; }); } else { NSLog(@"%@ Failed to load the tracks.", self); } }]; } @end 

AVAssetImageGenerator est le meilleur moyen d'instantané d'une video, cette méthode renvoie de manière asynchronous un UIImage :

 var player:AVPlayer? = // ... func screenshot(handler:((UIImage)->Void)) { guard let player = player , let asset = player.currentItem?.asset else { return } let imageGenerator = AVAssetImageGenerator(asset: asset) imageGenerator.appliesPreferredTrackTransform = true let times = [NSValue(CMTime:player.currentTime())] imageGenerator.generateCGImagesAsynchronouslyForTimes(times) { _, image, _, _, _ in if image != nil { handler(UIImage(CGImage: image!)) } } } 

(C'est Swift 2.3)