How to detect when AVPlayer video ends playing?
IosIphoneSwiftAvplayerIos Problem Overview
I'am using AVPlayer for playing local video file (mp4) in Swift. Does anyone know how to detect when video finish with playing? Thanks
Ios Solutions
Solution 1 - Ios
To get the AVPlayerItemDidPlayToEndTimeNotification
your object needs to be an AVPlayerItem
.
To do so, just use the .currentItem
property on your AVPlayer
Now you will get a notification once the video ends!
See my example:
let videoPlayer = AVPlayer(URL: url)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidFinishPlaying:",
name: AVPlayerItemDidPlayToEndTimeNotification, object: videoPlayer.currentItem)
func playerDidFinishPlaying(note: NSNotification) {
print("Video Finished")
}
Swift 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")
}
Don't forget to remove the Observer in your deinit
Swift 4, 5
NotificationCenter.default
.addObserver(self,
selector: #selector(playerDidFinishPlaying),
name: .AVPlayerItemDidPlayToEndTime,
object: videoPlayer.currentItem
)
Solution 2 - Ios
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")
}
Solution 3 - Ios
Swift 4.2 Version:
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)
}
Solution 4 - Ios
For SWIFT 3.0 This is working fine
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")
}
}
Solution 5 - Ios
Swift 4.0
This one works for me. Thanks to @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")
}
Solution 6 - Ios
If you fancy using Combine:
private var cancelBag: Set<AnyCancellable> = []
NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
.sink { _ in
player.seek(to: CMTime.zero)
player.play()
}
.store(in: &cancelBag)
Solution 7 - Ios
2019
It's really this simple
NotificationCenter.default.addObserver(
self,
selector: #selector(fileComplete),
name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
object: nil
)
(It's fine for the object to be nil.)
and then
@objc func fileComplete() {
print("IT'S DONE!")
}
Solution 8 - Ios
Swift 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
}
Solution 9 - Ios
For SWIFT 3.0
Here 'fullUrl' is the URL of the video and make sure that there would be no space in the URL, You should replace 'Space' with '%20' so that URL will work file.
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)
}
Add this below given method in your view controller.
func playerDidFinishPlaying(){
print("Video Finished playing in style")
}
Solution 10 - Ios
I know there are a lot of accepted answers here...
But, another route might be to add a boundary time observer to your AVPlayer. You would have to have the duration of the video, which you can get from your player.currentItem
, and then add it as your desired time boundary.
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
}
Solution 11 - Ios
In Swift 3 and RxSwift 3.5 all you have to do is:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.rx.notification(Notification.Name.AVPlayerItemDidPlayToEndTime)
.asObservable().subscribe(onNext: { [weak self] notification in
//Your action
}).addDisposableTo(disposeBag)
}
Solution 12 - Ios
Using Combine
, and also making sure the notification comes from the AVPlayerItem you are interested in and not just any. I am playing multiple items at once, so this would work in that scenario as well.
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)
Solution 13 - Ios
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: nil, queue: .main) { noti in
guard let item = noti.object as? AVPlayerItem else{
return
}
//DidPlayToEndTime
}
Solution 14 - Ios
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
}