Boucler une video avec AVFoundation AVPlayer?

Existe-t-il un moyen relativement simple de mettre en boucle une video dans AVFoundation?

J'ai créé mon AVPlayer et AVPlayerLayer comme suit:

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

puis je joue ma video avec:

 [avPlayer play]; 

La video joue bien mais s'arrête à la fin. Avec MPMoviePlayerController tout ce que vous avez à faire est de définir sa propriété repeatMode à la bonne valeur. Il ne semble pas y avoir de propriété similaire sur AVPlayer. Il ne semble pas non plus y avoir de callback qui me dira quand le film sera fini, je pourrai chercher au début et le rejouer.

Je n'utilise pas MPMoviePlayerController car il a de sérieuses limitations. Je veux être capable de lire plusieurs stream video à la fois.

Vous pouvez get une notification lorsque le joueur se termine. Vérifiez AVPlayerItemDidPlayToEndTimeNotification

Lors de la configuration du lecteur:

ObjC

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

Cela empêchera le joueur de faire une pause à la fin.

dans la notification:

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

cela va rembobiner le film.

N'oubliez pas de ne pas save la notification lorsque vous relâchez le lecteur.

Rapide

 NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd(notification:)), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: avPlayer?.currentItem) @objc func playerItemDidReachEnd(notification: Notification) { if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem { playerItem.seek(to: kCMTimeZero, completionHandler: nil) } } 

Si cela vous aide, dans iOS / tvOS 10, il y a un nouveau AVPlayerLooper () que vous pouvez utiliser pour créer une boucle parfaite de video (Swift):

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

Ceci a été présenté à la WWDC 2016 dans "Advances in AVFoundation Playback": https://developer.apple.com/videos/play/wwdc2016/503/

Même en utilisant ce code, j'ai eu un hoquet jusqu'à ce que je dépose un rapport de bug avec Apple et ai eu cette réponse:

Le file video ayant une durée de film plus longue que les pistes audio / video est le problème. FigPlayer_File désactive la transition sans transition car la modification de la piste audio est plus courte que la durée du film (15.682 vs 15.787).

Vous devez soit fixer les files de film pour avoir la durée du film et la durée des pistes à la même longueur ou vous pouvez utiliser le paramètre de plage de time de AVPlayerLooper (définir la plage de time de 0 à la durée de la piste audio)

Il s'avère que Premiere exportait des files avec une piste audio d'une longueur légèrement différente de la video. Dans mon cas, il était bien d'enlever complètement l'audio, et cela a réglé le problème.

Dans Swift :

Vous pouvez get une notification lorsque le joueur termine … vérifier AVPlayerItemDidPlayToEndTimeNotification

lors de la configuration du lecteur:

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

Cela empêchera le joueur de faire une pause à la fin.

dans la notification:

 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) 

cela va rembobiner le film.

N'oubliez pas de ne pas save la notification lorsque vous relâchez le lecteur.

Voici ce que j'ai fini par faire pour éviter le problème de pause-hoquet:

 __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]; }]; 

et retiré le self d'observer quand il n'est plus nécessaire.

REMARQUE: je n'ai pas utilisé avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;

Je recommand d'utiliser AVQueuePlayer pour boucler vos videos en toute transparence. Ajouter l'observateur de notification

 AVPlayerItemDidPlayToEndTimeNotification 

et dans son sélecteur, bouclez votre video

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

Pour éviter l'écart lorsque la video est rembobinée, l'utilisation de plusieurs copys du même contenu dans une composition a bien fonctionné pour moi. Je l'ai trouvé ici: http://www.developers-life.com/avplayer-looping-video-without-hiccupdelays.html (lien maintenant mort).

 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 copys. [tComposition insertTimeRange:tEditRange ofAsset:tAsset atTime:tComposition.duration error:nil]; } AVPlayerItem *tAVPlayerItem = [[AVPlayerItem alloc] initWithAsset:tComposition]; AVPlayer *tAVPlayer = [[AVPlayer alloc] initWithPlayerItem:tAVPlayerItem]; 

cela a fonctionné pour moi sans problèmes de hoquet, le point est en pause le joueur avant d'appeler la méthode 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. notification d'logging

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

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

Après avoir chargé la video dans l'AVPlayer (via son AVPlayerItem, bien sûr):

  [self addDidPlayToEndTimeNotificationForPlayerItem:item]; 

La méthode 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]; }]; } 

Dans votre méthode viewWillDisappear:

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

À votre vue, la déclaration de l'interface du controller dans le file d'implémentation:

 id _notificationToken; 

Besoin de voir cette opération en cours avant d'essayer? Téléchargez et exécutez cet exemple d'application:

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

Dans mon application, qui utilise ce même code, il n'y a aucune pause entre la fin de la video et le début. En fait, selon la video, il n'y a aucun moyen pour moi de dire que la video est au début, sauvegarder l'affichage du code temporel.

vous pouvez append un observateur AVPlayerItemDidPlayToEndTimeNotification et rejouer la video depuis le début dans le sélecteur, code comme ci-dessous

  //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]; } 

ma solution dans l'objective-c avec AVQueuePlayer – il semble que vous devez dupliquer AVPlayerItem et à la fin de la lecture du premier élément append instantanément une autre copy. "Kind of" a du sens et fonctionne pour moi sans aucun hoquet

 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

utiliser AVPlayerViewController ci-dessous le code, son travail pour moi

  let type : Ssortingng! = "mp4" let targetURL : Ssortingng? = 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() 

Tous les controls à montrer, espérons que c'est utile

 /* "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; 

Cette propriété est déjà définie dans AVAudioPlayer . J'espère que cela peut vous aider. J'utilise Xcode 6.3.