¿Colocando un video con AVFoundation AVPlayer?

¿Hay una forma relativamente fácil de hacer un video en AVFoundation?

He creado mi AVPlayer y AVPlayerLayer así:

avPlayer = [[AVPlayer playerWithURL:videoUrl] retain]; avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain]; avPlayerLayer.frame = contentView.layer.bounds; [contentView.layer addSublayer: avPlayerLayer]; 

y luego toco mi video con:

 [avPlayer play]; 

El video funciona bien pero se detiene al final. Con el MPMoviePlayerController todo lo que tiene que hacer es establecer su propiedad repeatMode en el valor correcto. No parece haber una propiedad similar en AVPlayer. También parece que no hay una callback que me avise cuando la película haya terminado, así que puedo search el principio y reproducirlo nuevamente.

No uso MPMoviePlayerController porque tiene algunas limitaciones serias. Quiero poder reproducir múltiples secuencias de video a la vez.

Puede get una Notificación cuando el jugador finaliza … verifique AVPlayerItemDidPlayToEndTimeNotification

al configurar el reproductor:

  avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:[avPlayer currentItem]]; 

Esto evitará que el jugador haga una pausa al final.

en la notificación:

 - (void)playerItemDidReachEnd:(NSNotification *)notification { AVPlayerItem *p = [notification object]; [p seekToTime:kCMTimeZero]; } 

Esto rebobinará la película.

No olvide que anula el logging de la notificación cuando suelta el reproductor.

Si ayuda, en iOS / tvOS 10, hay un nuevo AVPlayerLooper () que puedes usar para crear un bucle sin interrupciones de video (Swift):

 player = AVQueuePlayer() playerLayer = AVPlayerLayer(player: player) playerItem = AVPLayerItem(url: videoURL) playerLooper = AVPlayerLooper(player: player, templateItem: playerItem) player.play() 

Esto fue presentado en WWDC 2016 en "Avances en la reproducción de AVFoundation": https://developer.apple.com/videos/play/wwdc2016/503/

Incluso usando este código, tuve un problema hasta que presenté un informe de error con Apple y obtuve esta respuesta:

El problema es el file de película que tiene una duración de película más larga que las pistas de audio / video. FigPlayer_File está desactivando la transición sin espacios porque la edición de la pista de audio es más corta que la duración de la película (15.682 vs 15.787).

Debe arreglar los files de película para que la duración de la película y la duración de la pista sean la misma duración, o puede usar el parámetro de intervalo de time de AVPlayerLooper (establecer el intervalo de time de 0 a la duración de la pista de audio)

Resultó que Premiere había estado exportando files con una pista de audio de una longitud ligeramente diferente a la del video. En mi caso, estaba bien eliminar el audio por completo, y eso solucionó el problema.

En Swift :

Puede get una Notificación cuando el jugador finaliza … verifique AVPlayerItemDidPlayToEndTimeNotification

al configurar el reproductor:

 avPlayer.actionAtItemEnd = AVPlayerActionAtItemEnd.None NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerItemDidReachEnd:", name: AVPlayerItemDidPlayToEndTimeNotification, object: avPlayer.currentItem) 

Esto evitará que el jugador haga una pausa al final.

en la notificación:

 func playerItemDidReachEnd(notification: NSNotification) { if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem { playerItem.seekToTime(kCMTimeZero) } } 

Swift3

 NotificationCenter.default.addObserver(self, selector: #selector(PlaylistViewController.playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: avPlayer?.currentItem) 

Esto rebobinará la película.

No olvide que anula el logging de la notificación cuando suelta el reproductor.

Esto es lo que terminé haciendo para evitar el problema de pausa-hipo:

 __weak typeof(self) weakSelf = self; // prevent memory cycle NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter]; [noteCenter addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:nil // any object can send queue:nil // the queue of the sending usingBlock:^(NSNotification *note) { // holding a pointer to avPlayer to reuse it [weakSelf.avPlayer seekToTime:kCMTimeZero]; [weakSelf.avPlayer play]; }]; 

y eliminó el self de la observación cuando ya no es necesario.

NOTA: No avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;

Recomiendo utilizar AVQueuePlayer para enlazar tus videos a la perfección. Agregar el observador de notifications

 AVPlayerItemDidPlayToEndTimeNotification 

y en su selector, organice su video

 AVPlayerItem *video = [[AVPlayerItem alloc] initWithURL:videoURL]; [self.player insertItem:video afterItem:nil]; [self.player play]; 

Para evitar la brecha cuando se rebobina el video, el uso de varias copys del mismo activo en una composition funcionó bien para mí. Lo encontré aquí: http://www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html (enlace ahora muerto).

 AVURLAsset *tAsset = [AVURLAsset assetWithURL:tURL]; CMTimeRange tEditRange = CMTimeRangeMake(CMTimeMake(0, 1), CMTimeMake(tAsset.duration.value, tAsset.duration.timescale)); AVMutableComposition *tComposition = [[[AVMutableComposition alloc] init] autorelease]; for (int i = 0; i < 100; i++) { // Insert some copies. [tComposition insertTimeRange:tEditRange ofAsset:tAsset atTime:tComposition.duration error:nil]; } AVPlayerItem *tAVPlayerItem = [[AVPlayerItem alloc] initWithAsset:tComposition]; AVPlayer *tAVPlayer = [[AVPlayer alloc] initWithPlayerItem:tAVPlayerItem]; 

esto funcionó para mí sin problemas de hipo, el punto es pausar el reproductor antes de llamar al método seekToTime:

  1. init AVPlayer

     let url = NSBundle.mainBundle().URLForResource("loop", withExtension: "mp4") let playerItem = AVPlayerItem(URL: url!) self.backgroundPlayer = AVPlayer(playerItem: playerItem) let playerLayer = AVPlayerLayer(player: self.backgroundPlayer) playerLayer.frame = CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height) self.layer.addSublayer(playerLayer) self.backgroundPlayer!.actionAtItemEnd = .None self.backgroundPlayer!.play() 
  2. logging de notificación

     NSNotificationCenter.defaultCenter().addObserver(self, selector: "videoLoop", name: AVPlayerItemDidPlayToEndTimeNotification, object: self.backgroundPlayer!.currentItem) 
  3. function videoLoop

     func videoLoop() { self.backgroundPlayer?.pause() self.backgroundPlayer?.currentItem?.seekToTime(kCMTimeZero) self.backgroundPlayer?.play() } 

Después de cargar el video en el AVPlayer (a través de su AVPlayerItem, por supuesto):

  [self addDidPlayToEndTimeNotificationForPlayerItem:item]; 

El método addDidPlayToEndTimeNotificationForPlayerItem:

 - (void)addDidPlayToEndTimeNotificationForPlayerItem:(AVPlayerItem *)item { if (_notificationToken) _notificationToken = nil; /* Setting actionAtItemEnd to None prevents the movie from getting paused at item end. A very simplistic, and not gapless, looped playback. */ _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; _notificationToken = [[NSNotificationCenter defaultCenter] addObserverForName:AVPlayerItemDidPlayToEndTimeNotification object:item queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { // Simple item playback rewind. [[_player currentItem] seekToTime:kCMTimeZero]; }]; } 

En su vista, el método WillDisappear:

 if (_notificationToken) { [[NSNotificationCenter defaultCenter] removeObserver:_notificationToken name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem]; _notificationToken = nil; } 

En la statement de la interfaz de su controller de vista dentro del file de implementación:

 id _notificationToken; 

¿Necesita ver esto en marcha antes de intentarlo? Descargue y ejecute esta aplicación de ejemplo:

https://developer.apple.com/library/prerelease/ios/samplecode/AVBasicVideoOutput/Listings/AVBasicVideoOutput_APLViewController_m.html#//apple_ref/doc/uid/DTS40013109-AVBasicVideoOutput_APLViewController_m-DontLinkElementID_8

En mi aplicación, que usa este mismo código, no hay pausa alguna entre el final del video y el comienzo. De hecho, según el video, no hay forma de que diga que el video está al comienzo nuevamente, guarde la pantalla del código de time.

puede agregar un observador AVPlayerItemDidPlayToEndTimeNotification y reproducir video desde el inicio en el selector, código como el siguiente

  //add observer [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:_aniPlayer.currentItem]; -(void)playbackFinished:(NSNotification *)notification{ [_aniPlayer seekToTime:CMTimeMake(0, 1)];//replay from start [_aniPlayer play]; } 

mi solución en el objective -c con AVQueuePlayer- parece que tienes que duplicar el AVPlayerItem y al terminar la reproducción del primer elemento agrega instantáneamente otra copy. "Tipo de" tiene sentido y funciona para mí sin ningún contratime

 NSURL *videoLoopUrl; // as [[NSBundle mainBundle] URLForResource:@"assets/yourVideo" withExtension:@"mp4"]]; AVQueuePlayer *_loopVideoPlayer; +(void) nextVideoInstance:(NSNotification*)notif { AVPlayerItem *currItem = [AVPlayerItem playerItemWithURL: videoLoopUrl]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nextVideoInstance:) name:AVPlayerItemDidPlayToEndTimeNotification object: currItem]; [_loopVideoPlayer insertItem:currItem afterItem:nil]; [_loopVideoPlayer advanceToNextItem]; } +(void) initVideoPlayer { videoCopy1 = [AVPlayerItem playerItemWithURL: videoLoopUrl]; videoCopy2 = [AVPlayerItem playerItemWithURL: videoLoopUrl]; NSArray <AVPlayerItem *> *dummyArray = [NSArray arrayWithObjects: videoCopy1, videoCopy2, nil]; _loopVideoPlayer = [AVQueuePlayer queuePlayerWithItems: dummyArray]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(nextVideoInstance:) name: AVPlayerItemDidPlayToEndTimeNotification object: videoCopy1]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(nextVideoInstance:) name: AVPlayerItemDidPlayToEndTimeNotification object: videoCopy2]; } 

https://gist.github.com/neonm3/06c3b5c911fdd3ca7c7800dccf7202ad

use AVPlayerViewController debajo del código, funciona para mí

  let type : String! = "mp4" let targetURL : String? = NSBundle.mainBundle().pathForResource("Official Apple MacBook Air Video YouTube", ofType: "mp4") let videoURL = NSURL(fileURLWithPath:targetURL!) let player = AVPlayer(URL: videoURL) let playerController = AVPlayerViewController() playerController.player = player self.addChildViewController(playerController) self.playView.addSubview(playerController.view) playerController.view.frame = playView.bounds player.play() 

Todos los controles que se mostrarán, esperamos que sea útil

 /* "numberOfLoops" is the number of times that the sound will return to the beginning upon reaching the end. A value of zero means to play the sound just once. A value of one will result in playing the sound twice, and so on.. Any negative number will loop indefinitely until stopped. */ @property NSInteger numberOfLoops; 

Esta propiedad ya está definida dentro de AVAudioPlayer . Espero que esto le pueda ayudar. Estoy usando Xcode 6.3.