如何检测 AVPlayer 视频何时结束播放?

Posted

技术标签:

【中文标题】如何检测 AVPlayer 视频何时结束播放?【英文标题】:How to detect when AVPlayer video ends playing? 【发布时间】:2015-04-01 08:51:50 【问题描述】:

我正在使用 AVPlayer 在 Swift 中播放本地视频文件 (mp4)。 有谁知道如何检测视频何时播放完毕? 谢谢

【问题讨论】:

ObjC 示例,但您可以使用相同的方式:***.com/questions/6837002/… 【参考方案1】:

要获得AVPlayerItemDidPlayToEndTimeNotification,您的对象需要是AVPlayerItem

为此,只需在您的AVPlayer

上使用 .currentItem 属性

现在,您将在视频结束后收到通知!

看我的例子:

let videoPlayer = AVPlayer(URL: url)       

NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidFinishPlaying:",
        name: AVPlayerItemDidPlayToEndTimeNotification, object: videoPlayer.currentItem)

func playerDidFinishPlaying(note: NSNotification) 
    print("Video Finished")

斯威夫特 3

let videoPlayer = AVPlayer(URL: url)       

NotificationCenter.default.addObserver(self, selector: Selector(("playerDidFinishPlaying:")), 
       name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: videoPlayer.currentItem)

func playerDidFinishPlaying(note: NSNotification) 
    print("Video Finished")

不要忘记删除 deinit 中的观察者

斯威夫特 4、5

NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: nil)

【讨论】:

你知道用这种方法和用AVPlayer方法addPeriodicTimeObserver(TimeInterval:)有什么区别吗? 请问如何删除 来自developer.apple.com/library/archive/releasenotes/Foundation/… 在 OS X 10.11 和 ios 9.0 中,NSNotificationCenter 和 NSDistributedNotificationCenter 将不再向可能被释放的已注册观察者发送通知。 object 参数传递 currentItem 是重要的部分 - 检查是否有问题。 在两个方面错了 (1) 实际上你多年来不需要将任何东西作为对象传递 (2),不需要取消通知【参考方案2】:

Swift 3.0

let videoPlayer = AVPlayer(URL: url)

NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying(note:)),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)

@objc func playerDidFinishPlaying(note: NSNotification)
        print("Video Finished")
    

【讨论】:

但它说 [myClass playerDidFinishPlaying:]: unrecognized selector sent to instance #selector(playerDidFinishPlaying) 多次使用这个addObserver会不会导致内存泄露? 我应该删除观察者吗?我该怎么做?【参考方案3】:

Swift 4.2 版本:

var player: AVPlayer!
  //
  //
// Configure Player
override func viewDidAppear(_ animated: Bool) 
    super.viewDidAppear(animated)

    let filepath: String? = Bundle.main.path(forResource: "selectedFileName", ofType: "mp4")
    if let filepath = filepath 
        let fileURL = URL.init(fileURLWithPath: filepath)
        player = AVPlayer(url: fileURL)
        let playerLayer = AVPlayerLayer(player: player)
        // Register for notification
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(playerItemDidReachEnd),
                                                         name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                                         object: nil) // Add observer

        playerLayer.frame = self.view.bounds
        self.view.layer.addSublayer(playerLayer)
        player.play()
    

// Notification Handling
@objc func playerItemDidReachEnd(notification: NSNotification) 
    player.seek(to: CMTime.zero)
    player.play()

// Remove Observer
deinit 
    NotificationCenter.default.removeObserver(self)

【讨论】:

2019-4-20,XCode 10.2.1,Swift 5,为我工作,谢谢【参考方案4】:

对于 SWIFT 3.0 这工作正常

class PlayVideoViewController: UIViewController 
    override func viewDidLoad() 
        super.viewDidLoad()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PlayVideoViewController.finishVideo), name: NSNotification.Name.AVPlayerItemDidPlayToEndTimeNotification, object: nil)
    

    func finishVideo()
    
        print("Video Finished")
    

【讨论】:

nil 是一个神奇的词,否则你必须添加/删除你添加的每个项目?【参考方案5】:

Swift 4.0

这个对我有用。感谢@Channel

    private func playVideo(fileURL: String) 

            // Create RUL object
            let url = URL(string: fileURL)

            // Create Player Item object
            let playerItem: AVPlayerItem = AVPlayerItem(url: url!)
            // Assign Item to Player
            let player = AVPlayer(playerItem: playerItem)

            // Prepare AVPlayerViewController
            let videoPlayer = AVPlayerViewController()
            // Assign Video to AVPlayerViewController
            videoPlayer.player = player

            NotificationCenter.default.addObserver(self, selector: #selector(myViewController.finishVideo), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)

            // Present the AVPlayerViewController
            present(videoPlayer, animated: true, completion: 
                    // Play the Video
                    player.play()
            )

    

    @objc func finishVideo()
    
            print("Video Finished")
    

【讨论】:

【参考方案6】:

如果您喜欢使用 Combine:

private var cancelBag: Set<AnyCancellable> = []

NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
.sink  _ in
    player.seek(to: CMTime.zero)
    player.play()

.store(in: &cancelBag)

【讨论】:

【参考方案7】:

2019

就这么简单

NotificationCenter.default.addObserver(
    self,
    selector: #selector(fileComplete),
    name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
    object: nil
)

(对象为零也可以。)

然后

@objc func fileComplete() 
    print("IT'S DONE!")

【讨论】:

【参考方案8】:

斯威夫特 3.0

let videoPlayer = AVPlayer(URL: url)
NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying(note:)),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)

func playerDidFinishPlaying(note: NSNotification)
    //Called when player finished playing

【讨论】:

【参考方案9】:

对于 SWIFT 3.0

这里的 'fullUrl' 是视频的 URL,并确保 URL 中没有空格,您应该将 'Space' 替换为 '%20' 以便 URL 可以工作文件。

  let videoURL = NSURL(string: fullUrl)
  let player = AVPlayer(url: videoURL! as URL)

  playerViewController.delegate = self
  playerViewController.player = player
  self.present(playerViewController, animated: false) 

    self.playerViewController.player!.play()

    NotificationCenter.default.addObserver(self, selector: #selector(yourViewControllerName.playerDidFinishPlaying), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)
  

在您的视图控制器中添加以下给定方法。

func playerDidFinishPlaying()    
print("Video Finished playing in style")

【讨论】:

多次使用这个addObserver会不会导致内存泄露?【参考方案10】:

我知道这里有很多公认的答案...

但是,另一种方法可能是将边界时间观察器添加到您的 AVPlayer。您必须拥有视频的持续时间,您可以从 player.currentItem 获得,然后将其添加为您想要的时间边界。

fileprivate var videoEndObserver: Any?

func addVideoEndObserver() 
    guard let player = YOUR_VIDEO_PLAYER else  return 

    // This is just in case you are loading a video from a URL.
    guard let duration = player.currentItem?.duration, duration.value != 0 else 
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute:  [weak self] in
            self?.addVideoEndObserver()
        )

        return
    

    let endTime = NSValue(time: duration - CMTimeMakeWithSeconds(0.1, duration.timescale))
    videoEndObserver = player.addBoundaryTimeObserver(forTimes: [endTime], queue: .main, using: 
        self.removeVideoEndObserver()

        // DO YOUR STUFF HERE...
    )


func removeVideoEndObserver() 
    guard let observer = videoEndObserver else  return 

    videoPlayer.player?.removeTimeObserver(observer)
    videoEndObserver = nil

【讨论】:

这是个好主意,在实践中这样做通常会更好。对于较长的材料(歌曲等),通常最好在结束前一秒(或者尽可能少)捕捉它,而不是在文件实际“结束”时处理整个(未记录的)Apple混乱。【参考方案11】:

在 Swift 3 和 RxSwift 3.5 中,您所要做的就是:

override func viewDidLoad() 
    super.viewDidLoad()

    NotificationCenter.default.rx.notification(Notification.Name.AVPlayerItemDidPlayToEndTime)
        .asObservable().subscribe(onNext:  [weak self] notification in

            //Your action

        ).addDisposableTo(disposeBag)

【讨论】:

【参考方案12】:

使用Combine,并确保通知来自您感兴趣的 AVPlayerItem 而不仅仅是任何一个。我一次玩多个项目,所以这也适用于那种情况。

private var subscriptions: Set<AnyCancellable> = []

NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
    .receive(on: RunLoop.main)
    .sink  [weak self] notification in
        guard let item = notification.object as? AVPlayerItem else  return 
        if item == self?.player.currentItem 
         //.... Here you know it was the item you are interested in that played to end and not just any
        
    
    .store(in: &subscriptions)

【讨论】:

以上是关于如何检测 AVPlayer 视频何时结束播放?的主要内容,如果未能解决你的问题,请参考以下文章

在锁定屏幕上检测播放结束 - AVPlayer

如何在 Swift 3 中检查 AV 播放器何时达到特殊秒数

如何检测 iframe 视频何时结束?没有 API

检测文件流已完成并准备好使用 avplayer 播放

Youtube API如何在视频结束时从0秒开始播放视频?框架,Javascript

如何知道 AVPlayer 何时使用仅音频比特率?