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

问题描述 投票:0回答:17

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

ios iphone swift avplayer
17个回答
136
投票

要获得

AVPlayerItemDidPlayToEndTimeNotification
,您的对象需要是
 AVPlayerItem

为此,只需使用 .currentItem

 上的 
AVPlayer

属性即可

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

看我的例子:

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: videoPlayer.currentItem
)

41
投票

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

17
投票

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

10
投票

如果您喜欢使用组合:

private var cancelBag: Set<AnyCancellable> = []

NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
.sink { _ in
    player.seek(to: CMTime.zero)
    player.play()
}
.store(in: &cancelBag)

8
投票

适用于 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")
    }
}

8
投票

斯威夫特4.0

这个对我有用。感谢@频道

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

7
投票

SWIFT 5 更新

带有 @objc 函数的观察者方法不是原生的。最好在 swift 5 中使用事件发布器。非常简单。

在结构中声明以下内容:

var pub = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)

然后在任何视图上添加

.onReceive(pub) { (output) in
    print("Video Finished")
}

6
投票

2023

其实就是这么简单

override func viewDidLoad() {
    
    super.viewDidLoad()
    
    // Note, somewhat bizarrely, it's perfectly OK to add
    // the end time notification once for a view controller,
    // and even before you create the player, etc. See notes.

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

    ...

object
(调用的最后一个参数)为零就可以了。)

然后

@objc func fileComplete() {
    print("one video has reached -0 time")
}

你只需将其放入 viewDidLoad 中即可。

这就是为什么......

如果您播放多个视频:

如果您多次运行上述代码,则每次视频结束时您都会收到多个通知。即“fileComplete”将被多次调用。

通常你的代码看起来像..

  • 用户点击播放视频 X
  • 播放特定视频的代码
  • 用户点击以更改为视频 Y
  • 播放特定视频的代码

有点烦人的是,您必须安排调用有问题的代码,

.addObserver
整个系统只能调用一次

并且,是的,即使您还没有打开 AVPlayer,也完全可以

.addObserver

事实上,最好的地方就是 viewDidLoad - 确保它只运行一次。

如果使用示例中的代码,则不需要删除观察者

多年来,在 iOS 中,当屏幕消失时,您不需要删除观察者。 Apple 对此有完整记录。

需要明确的是,如果您使用

addObserver
的块形式,那么完成后您仍然必须删除观察者。使用 addObserver 的选择器形式(如此处的代码示例所示),完成后不需要显式删除观察者。


4
投票

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

3
投票

适用于 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")
}

3
投票

我知道这里有很多被接受的答案......

但是,另一种方法可能是向 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
}

3
投票
func shareEditedVedio() -> AVPlayer {
    let editedVedioPlayer = AVPlayer(url: self.vedioData.vedioURLWithAddedSounds!)
    NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying(note:)),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: editedVedioPlayer.currentItem)
    return editedVedioPlayer
}


@objc func playerDidFinishPlaying(note: NSNotification){
    //Called when player finished playing
}

2
投票

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

1
投票

使用

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)

1
投票
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: nil, queue: .main) { noti in
        guard let item = noti.object as? AVPlayerItem else{
            return
        }
        //DidPlayToEndTime
    }

0
投票

我遇到了通知从未被调用的问题,在 AVPlayerViewController 的演示文稿中设置通知为我解决了这个问题:

func presentVideo(url:URL) {
        let player = AVPlayer(url: url)
        let playerViewController = AVPlayerViewController()
        playerViewController.player = player
        
        self.present(playerViewController, animated: true) {
            DispatchQueue.main.async {
                playerViewController.player!.play()
                //NOTE: The notification must be created here for it to work as expected
                NotificationCenter.default.addObserver(self, selector: #selector(self.videoDidEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
            }
        }
    }

-1
投票

另一种解决方案:

player.observe(\AVPlayer.actionAtItemEnd) { player, _ in
    print("video did end")
}
© www.soinside.com 2019 - 2024. All rights reserved.