如何在Swift中循环AVPlayer?


71

一个简单的问题,由于某种原因,我似乎找不到答案。

如何在Swift中循环AVPlayer?

numberOfLoops = -1仅适用于AVAudioPlayer

我确实需要它无任何延迟地循环/黑色闪光等。这就是为什么我不使用MPMoviePlayerViewController的原因。

谢谢你的帮助。

码:

    let url_1 = NSURL.fileURLWithPath(outputFilePath_1)

    let asset_1 = AVAsset.assetWithURL(url_1) as? AVAsset
    let playerItem_1 = AVPlayerItem(asset: asset_1)

    let player_1 = AVPlayer(playerItem: self.playerItem_1)

    let playerLayer_1 = AVPlayerLayer(player: self.player_1)

    playerLayer_1!.frame = self.view.frame

    self.view.layer.addSublayer(self.playerLayer_1)

    player_1!.play()

3
AVPlayer使用通知而不是属性。本质上,您需要听视频确实结束事件,然后寻找视频的开头。下面是一个例子(目标C,但你的想法):stackoverflow.com/questions/5361145/...
Msencenb

Answers:


173

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

以上所有功能均可在任何iOS版本中使用。但是,对于<iOS 10.0,这是唯一的解决方案。

斯威夫特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()
}

迅捷3

NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { [weak self] _ in
    self?.player?.seek(to: kCMTimeZero)
    self?.player?.play()
}

迅捷2

配置AVPlayerItem并创建播放器后:

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

别忘了 import AVFoundation


4
您可以添加,queue: .main然后就不需要分派到主队列中。
大卫·贝克

这是真正的答案,其他答案对我不起作用(速记3)。
changbenny

5秒钟后第一次发出此通知电话。如何处理呢?
Shahbaz Akram

1
在使用自己的迅捷3解决方案中。在调度块内部将导致保留对象。
glm4

1
斯威夫特4:NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: .main) { _ in self.player?.seek(to: CMTime.zero) self.player?.play() }
约旦

43

从iOS 10开始,无需使用通知即可循环播放视频,只需使用AVPlayerLooper即可,如下所示:

let asset = AVAsset(url: videoURL)
let item = AVPlayerItem(asset: asset)
let player = AVQueuePlayer(playerItem: item)
videoLooper = AVPlayerLooper(player: player, templateItem: item)

确保此循环程序是在函数作用域之外创建的,以防它在函数返回时停止。


4
感谢上帝。KVO只是做事的一种骇人听闻的方式。
AnBisw

该方法确实有效,但是我发现AVPlayerItemMetadataOutput附加到playerItem的任何对象都不会被触发(至少从iOS 11.4.1开始),这迫使我返回该AVPlayerItemDidPlayToEndTimeNotification方法
Jaysen Marais

应该是,let videoLooper否则你会得到的Use of unresolved identifier 'videoLooper'
亚历山大·弗兰尼克

4
@AlexanderFlenniken BrightFuture之所以没有“ let videoLooper”的原因是,正如他们所指出的,循环器需要保留在函数范围之外。因此,在类设置中的某个位置将存在一个“ var videoLooper”,而在此处的函数中您只是对其进行分配。如果您在设置功能内执行了“ let”操作,但未将其分配给该功能外的属性,则在播放视频时将无法进行循环播放。
Ben Stahl

嗨,我可以找到videoLooper的方法播放吗?
famfamfam

33

@Christopher Pickslay为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()
    }
}

但是,正如我在他的回答下面提到的那样,如果您有多个播放器,请确保将对象指定为AVPlayer的播放器项。


28

或者,您可以使用基于块的NSNotificationCenter API:

func loopVideo(videoPlayer: AVPlayer) {
    NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: nil, queue: nil) { notification in
        videoPlayer.seekToTime(kCMTimeZero)
        videoPlayer.play()
    }
}

很好的答案,非常简单。如果您有多个播放器,请确保将AVPlayer指定为“对象”参数,否则,每个注册播放器将在任何播放器完成时收到通知。
Ben Stahl

我正在avplayer中播放视频,并且它在后台运行,并且当我想播放另一个视频但不播放视频时
Mahesh Narla

2
更正@BenStahl的答案,您应该将指定AVPlayerItem为“对象”而不是AVPlayer。应该是videoPlayer.currentItem
Chan Jing Hong

24

好,我已经解决了。感谢Msencenb用我的Objective C答案指出正确的方向。

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

}

9

我已经设法在swift 3中为OSX创建了无缝的视频循环。它应该可以在iOS上运行,并且在swift 2上也不需要进行任何修改。

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

当您要更改视频时,只需再次使用不同的URL调用selectVideoWithLoop。


5

Swift 3.0:

首先检查视频终点:

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



func videoDidReachEnd() {

        //now use seek to make current playback time to the specified time in this case (O)
        let duration : Int64 = 0 (can be Int32 also)
        let preferredTimeScale : Int32 = 1
        let seekTime : CMTime = CMTimeMake(duration, preferredTimeScale)
        player!.seek(to: seekTime)
        player!.play()
    }

0

对话可能会晚一点,但是为了让我感到高兴(如果有人再来这里),我在这里找到了这个答案(这是SO的另一个答案),这对我很有用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.