Simple question I can\'t seem to find an answer to for some reason.
How do you loop AVPlayer in Swift?
numberOfLoops = -1 only works for AVAudioPlayer
<I've managed to create a seamless video looping for OSX in swift 3. It should work on iOS and with little modification on swift 2 as well.
var player : AVQueuePlayer!
// Looping video initial call
internal func selectVideoWithLoop(url : URL)
{
let asset = AVAsset(url: url)
player.pause()
let playerItem1 = AVPlayerItem(asset: asset)
let playerItem2 = AVPlayerItem(asset: asset)
player.removeAllItems()
player.replaceCurrentItem(with: playerItem1)
player.insert(playerItem2, after: playerItem1)
player.actionAtItemEnd = AVPlayerActionAtItemEnd.advance
player.play()
let selector = #selector(ViewController.playerItemDidReachEnd(notification:))
let name = NSNotification.Name.AVPlayerItemDidPlayToEndTime
// removing old observer and adding it again for sequential calls.
// Might not be necessary, but I like to unregister old notifications.
NotificationCenter.default.removeObserver(self, name: name, object: nil)
NotificationCenter.default.addObserver(self, selector: selector, name: name, object: nil)
}
// Loop video with threadmill pattern
// Called by NotificationCenter, don't call directly
func playerItemDidReachEnd(notification: Notification)
{
let item = player.currentItem!
player.remove(item)
item.seek(to: kCMTimeZero)
player.insert(item, after: nil)
}
When you want to change the video, just call selectVideoWithLoop with a different url again.
Alternatively, you can use the block-based NSNotificationCenter API:
func loopVideo(videoPlayer: AVPlayer) {
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: nil, queue: nil) { notification in
videoPlayer.seekToTime(kCMTimeZero)
videoPlayer.play()
}
}
@Christopher Pickslay's answer updated for Swift 4:
func loopVideo(videoPlayer: AVPlayer) {
NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: nil) { notification in
videoPlayer.seek(to: CMTime.zero)
videoPlayer.play()
}
}
But, as I mentioned below his answer, be sure to specify the object as the AVPlayer's player item if you have multiple players.
Might be a bit late to the conversation, but for the sake of upade (in case anyone comes here again), I found this answer HERE (it's another answer on SO) that worked for me.
Swift 5 (iOS 10.0+)
var playerLooper: AVPlayerLooper! // should be defined in class
var queuePlayer: AVQueuePlayer!
...
let asset: AVAsset = ... // AVAsset with its 'duration' property value loaded
let playerItem = AVPlayerItem(asset: asset)
self.queuePlayer = AVQueuePlayer(playerItem: playerItem)
// Create a new player looper with the queue player and template item
self.playerLooper = AVPlayerLooper(player: queuePlayer, templateItem: playerItem)
< iOS 10.0
All above works in any iOS version. But, for < iOS 10.0 it's the only solution.
Swift 4
var player: AVPlayer!
...
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: CMTime.zero)
self?.player?.play()
}
Swift 3
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: kCMTimeZero)
self?.player?.play()
}
Swift 2
After AVPlayerItem is configured and player is created:
var player: AVPlayer!
...
// Invoke after player is created and AVPlayerItem is specified
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "playerItemDidReachEnd:",
name: AVPlayerItemDidPlayToEndTimeNotification,
object: self.player.currentItem)
...
func playerItemDidReachEnd(notification: NSNotification) {
self.player.seekToTime(kCMTimeZero)
self.player.play()
}
Don't forget to import AVFoundation
OK I have worked it out. Thanks Msencenb for pointing me in the right direction with an Objective C answer.
player_1?.actionAtItemEnd = .None
//set a listener for when the video ends
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "restartVideoFromBeginning",
name: AVPlayerItemDidPlayToEndTimeNotification,
object: player_1?.currentItem)
//function to restart the video
func restartVideoFromBeginning() {
//create a CMTime for zero seconds so we can go back to the beginning
let seconds : Int64 = 0
let preferredTimeScale : Int32 = 1
let seekTime : CMTime = CMTimeMake(seconds, preferredTimeScale)
player_1!.seekToTime(seekTime)
player_1!.play()
}