iOS: AVPlayer: get una instantánea del marco actual de un video.

He pasado todo el día y he revisado muchas respuestas de SO, references de Apple, documentaciones, etc., pero no he tenido éxito.

Quiero algo simple: estoy reproduciendo un video con AVPlayer y quiero pausarlo y get el marco actual como UIImage . Eso es.

Mi video es un file m3u8 ubicado en internet, se juega normalmente en AVPlayerLayer sin ningún problema.

¿Qué he probado?

  1. AVAssetImageGenerator . No funciona, el método copyCGImageAtTime:actualTime: error: devuelve la image nula ref. De acuerdo con la respuesta aquí AVAssetImageGenerator no funciona para la transmisión de videos.
  2. Tomar una instantánea de la vista del jugador. Intenté primero renderInContext: en AVPlayerLayer , pero luego me di count de que no está representando este tipo de capas "especiales". Luego encontré un nuevo método introducido en iOS 7: drawViewHierarchyInRect:afterScreenUpdates: que debería ser capaz de representar también las capas especiales, pero sin suerte, todavía tenía la instantánea de la interfaz de usuario con un área negra en blanco donde se muestra el video.
  3. AVPlayerItemVideoOutput . He agregado una salida de video para mi AVPlayerItem , sin embargo cada vez que hasNewPixelBufferForItemTime: devuelve NO . Creo que el problema es la transmisión de video y no estoy solo con este problema.
  4. AVAssetReader Estaba pensando intentarlo pero decidí no perder el time después de encontrar una pregunta relacionada aquí .

Entonces, ¿no hay alguna forma de get una instantánea de algo que de todos modos estoy viendo en este momento en la pantalla? No puedo creer esto.

AVPlayerItemVideoOutput funciona bien para mí desde un m3u8. Tal vez sea porque no he consultado hasNewPixelBufferForItemTime y simplemente llamo copyPixelBufferForItemTime ? Este código produce un CVPixelBuffer lugar de un UIImage , pero hay respuestas que describen cómo hacerlo .

Esta respuesta en su mayor parte se obtuvo a partir de aquí.

 #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] initWithPixelBufferAttributes: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 URLWithString:@"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 es la mejor manera de AVAssetImageGenerator instantáneamente un video, este método devuelve de forma asíncrona 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.appliesPrefernetworkingTrackTransform = true let times = [NSValue(CMTime:player.currentTime())] imageGenerator.generateCGImagesAsynchronouslyForTimes(times) { _, image, _, _, _ in if image != nil { handler(UIImage(CGImage: image!)) } } } 

(Es Swift 2.3)