150 votes

Mettre en boucle une vidéo avec AVFoundation AVPlayer ?

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

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

avPlayer = [[AVPlayer playerWithURL:videoUrl] retain];
avPlayerLayer = [[AVPlayerLayer playerLayerWithPlayer:avPlayer] retain];

avPlayerLayer.frame = contentView.layer.bounds;
[contentView.layer addSublayer: avPlayerLayer];

et ensuite je joue ma vidéo avec :

[avPlayer play];

La vidéo est bien diffusée mais s'arrête à la fin. Avec le MPMoviePlayerController, tout ce que vous avez à faire, c'est de régler sa fonction 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 rappel pour me dire quand le film est terminé, afin que je puisse revenir au début et le relire.

Je n'utilise pas MPMoviePlayerController car il présente de sérieuses limitations. Je veux pouvoir lire plusieurs flux vidéo à la fois.

1 votes

Voir cette réponse pour un lien vers un code fonctionnel réel : stackoverflow.com/questions/7822808/

3voto

user2581875 Points 1

Pour éviter le décalage lorsque la vidéo est rembobinée, l'utilisation de plusieurs copies d'une même ressource dans une composition a bien fonctionné pour moi. Je l'ai trouvé ici : 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 copies.
    [tComposition insertTimeRange:tEditRange ofAsset:tAsset atTime:tComposition.duration error:nil];
}
AVPlayerItem *tAVPlayerItem = [[AVPlayerItem alloc] initWithAsset:tComposition];
AVPlayer *tAVPlayer = [[AVPlayer alloc] initWithPlayerItem:tAVPlayerItem];

3voto

Nicolai Harbo Points 161

SWIFT 5 :

private var player: AVPlayer?

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(restartVideo),
                                           name: .AVPlayerItemDidPlayToEndTime,
                                           object: self.player?.currentItem)
}

@objc func restartVideo() {
    player?.pause()
    player?.currentItem?.seek(to: CMTime.zero, completionHandler: { _ in
        self.player?.play()
    })
}

3voto

kevnguy Points 29

Je recommande l'utilisation d'AVQueuePlayer pour mettre vos vidéos en boucle de manière transparente. Ajouter l'observateur de notification

AVPlayerItemDidPlayToEndTimeNotification

et dans son sélecteur, mettez votre vidéo en boucle

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

0 votes

J'ai essayé cette méthode et elle ne présente aucune amélioration par rapport à la méthode suggérée par @Bastian. Avez-vous réussi à supprimer totalement le problème avec cette méthode ?

2 votes

@amadour ce que vous pouvez faire est d'ajouter 2 des mêmes vidéos dans le lecteur AVQueuePlayer lorsqu'il est initialisé et lorsque le lecteur affiche la notification AVPlayerItemDidPlayToEndTime, ajouter la même vidéo à la file d'attente du lecteur.

1voto

Vojta Points 457

Cela a fonctionné pour moi sans problème de hoquet, le but est de faire une pause le lecteur 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'enregistrement

    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()
    }

1voto

neon M3 Points 11

Ma solution en objective-c avec AVQueuePlayer - il semble que vous devez dupliquer l'AVPlayerItem et à la fin de la lecture du premier élément ajouter instantanément une autre copie. "C'est logique et cela fonctionne pour moi sans problème.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X