Dismiss video when it is finished playing Swift - ios

I am using the MPMoviePlayerController to play a video. I want to dismiss the video when it has finished playing. Here is my code:
import UIKit
import MediaPlayer
class programViewController: UIViewController {
var moviePlayer : MPMoviePlayerController?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
playVideo("video", type: "mov")
}
func playVideo(navn:String, type:String) {
let path = NSBundle.mainBundle().pathForResource(navn, ofType: type)
let url = NSURL.fileURLWithPath(path!)
moviePlayer = MPMoviePlayerController(contentURL: url)
if let player = moviePlayer {
player.view.frame = self.view.bounds
player.prepareToPlay()
player.scalingMode = .AspectFit
player.shouldAutoplay = true
player.fullscreen = false
self.view.addSubview(player.view)
}
}
}

You should add in playVideo function
NSNotificationCenter.defaultCenter().addObserver(self, selector: "movieFinishedCallback:", name: MPMoviePlayerPlaybackDidFinishNotification, object: player)
and in your classs:
func movieFinishedCallback(notif:NSNotification) {
// Obtain the reason why the movie playback finished
var userInfo:Dictionary<String,Int!> = notif.userInfo as! Dictionary<String,Int!>
let finishReason : Int = userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]!
// Dismiss the view controller ONLY when the reason is not "playback ended"
if ( finishReason == MPMovieFinishReason.PlaybackEnded.rawValue)
{
let moviePlayer:MPMoviePlayerController = notif.object as! MPMoviePlayerController
// Remove this class from the observers
NSNotificationCenter.defaultCenter().removeObserver(self, name: MPMoviePlayerPlaybackDidFinishNotification, object: self.moviePlayerController!.moviePlayer)
// Dismiss the view controller
moviePlayer.view.removeFromSuperview()
}
}

updated version of #roman-barzyczak's answer:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(movieFinishedCallback(_:)), name: MPMoviePlayerPlaybackDidFinishNotification, object: moviePlayer)
then:
func movieFinishedCallback(notification: NSNotification) {
if let userInfo = notification.userInfo as? [String : NSNumber] {
let reason = userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]
let finishReason = MPMovieFinishReason(rawValue: reason!.integerValue)
if (finishReason != MPMovieFinishReason.PlaybackEnded),
let moviePlayer = notification.object as? MPMoviePlayerController {
NSNotificationCenter.defaultCenter().removeObserver(self, name: MPMoviePlayerPlaybackDidFinishNotification, object: moviePlayer)
moviePlayer.view.removeFromSuperview()
}
}
}

You would need to listen for MPMoviePlayerPlaybackDidFinishNotification notification.
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playerPlaybackDidFinish"), name: MPMoviePlayerPlaybackDidFinishNotification, object: nil)
// Present
self.presentViewController(moviePlayer, animated: ture, completion: nil)
// Dismiss
moviewPlayer.dismissViewControllerAnimated(true, completion: nil)

It is possible just to delete the view from subviews, and because the last made subview is the movie player view you can delete it just by using this code:
self.view.subviews[self.view.subviews.count-1].removeFromSuperview()

Related

How to detect which video ended when using multiple AVPlayers?

I am playing two videos side by side on screen so I have two instances of AVPlayer. I am detecting end of video playback using notification which is working fine. My selector(playerDidFinishPlaying) is getting called for both videos when they end.
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: nil)
Now my problem is in the selector(playerDidFinishPlaying), I want to detect for which avplayer it got called? How can I uniquely identify the AVPlayer whose video ended?
Another idea. Updated #black_pearl's method
Different notification registration , with different notification methods.
var player = AVPlayer()
var playerTwo = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlay(_:)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishMusic(_:)), name: .AVPlayerItemDidPlayToEndTime, object: playerTwo.currentItem)
}
#objc func playerDidFinishPlay(_ noti: Notification) {
if let p = noti.object as? AVPlayerItem, p == player.currentItem {
print("1")
}
}
#objc func playerDidFinishMusic(_ noti: Notification) {
if let p = noti.object as? AVPlayerItem, p == playerTwo.currentItem{
print("2")
}
}
via the object of notification to uniquely identify,
post notification .AVPlayerItemDidPlayToEndTime with object of player.currentItem
NotificationCenter
.default
.addObserver(self,
selector: #selector(self.moviePlayBackFinished(sender:)),
name: .AVPlayerItemDidPlayToEndTime,
object: player.currentItem)
The easy way:
The following code works, the drawback is that when one player ends, the notify method will be called twice.
var player = AVPlayer()
var playerTwo = AVPlayer()
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlay(_:)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlay(_:)), name: .AVPlayerItemDidPlayToEndTime, object: playerTwo.currentItem)
}
#objc func playerDidFinishPlay(_ noti: Notification) {
if let p = noti.object as? AVPlayerItem, p == player.currentItem{
print("1")
}
if let p = noti.object as? AVPlayerItem, p == playerTwo.currentItem{
print("2")
}
}
The track state way:
With Notification, you can see that, there is a player ended.
You need to find the player.
The player must obey two rules, it has began , and it was over.
Use var hasPlay: (one: Bool, two: Bool), to find the player just playing.
Use isPlaying to find the player, no longer played.
var player = AVPlayer()
var playerTwo = AVPlayer()
var hasPlay: (one: Bool, two: Bool) = (false, false)
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlay), name: .AVPlayerItemDidPlayToEndTime, object: nil)
}
#IBAction func beepPressed(_ sender: UIButton) {
hasPlay.one = true
let url = // ...
player = AVPlayer(url: url!)
player.play()
}
#IBAction func beepPressedTwo(_ sender: UIButton) {
hasPlay.two = true
let url = // ...
playerTwo = AVPlayer(url: url!)
playerTwo.play()
}
#objc func playerDidFinishPlay() {
if player.isPlaying == false, hasPlay.one == true{
hasPlay.one = false
print("1")
}
if playerTwo.isPlaying == false, hasPlay.two == true{
hasPlay.two = false
print("2")
}
}
}
extension AVPlayer {
var isPlaying: Bool {
return rate != 0 && error == nil
}
}
Updated #dengST30's method one, with some ease.
with two notification registered, any end method will be called twice.
So it needs to add some mutually exclusive.
var player = AVPlayer()
var playerTwo = AVPlayer()
var justEnded: (one: Bool, two: Bool) = (false, false)
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlay(_:)), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlay(_:)), name: .AVPlayerItemDidPlayToEndTime, object: playerTwo.currentItem)
playButton.tintColor = .systemBlue
}
#objc func playerDidFinishPlay(_ noti: Notification) {
if let p = noti.object as? AVPlayerItem, p == player.currentItem {
if justEnded.one == false{
print("1")
}
justEnded.one.toggle()
}
if let p = noti.object as? AVPlayerItem, p == playerTwo.currentItem{
if justEnded.two == false{
print("2")
}
justEnded.two.toggle()
}
}

Getting MPMoviePlayerPlaybackDidFinishReasonUserInfoKey of MPMoviePlayerController nil

I have use MPMoviePlayerController in my app. I am trying to get Done button event of MPMoviePlayerController. But I am getting nil value in [MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] and because of it app is crashing. This is my code:
// Define notification in class
let notificationName = Notification.Name("moviePlayerDoneButtonClicked")
//---register and posted notification in viewDidLoad()
// Register to receive notification
NotificationCenter.default.addObserver(self, selector: #selector(PlayVideo.moviePlayerDoneButtonClicked), name: self.notificationName, object: nil)
// Post notification
NotificationCenter.default.post(name: self.notificationName, object: nil)
//invoking player
playVideo()
//Event for moviePlayer Done Button Pressed (method for notification)
func moviePlayerDoneButtonClicked(note: NSNotification) {
print ("delegate called....")
let reason = note.userInfo?[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]
if (MPMovieFinishReason(rawValue: reason as! Int) == MPMovieFinishReason.userExited) {
print ("Done button clicked.")
self.delegate.exitVideoPlayer()
}
}
//—code for
func playVideo() {
//--Adjust height and width of player as per device
activityIndicator.isHidden = false
activityIndicator.startAnimating()
print ("self.attachmentURL : \(self.attachmentURL)")
DispatchQueue.main.async {
let url:URL = URL(string: "https://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")!
self.moviePlayer = MPMoviePlayerController(contentURL: url)
self.moviePlayer!.view.frame = CGRect(x: 0, y: 0, width: self.width, height: self.height);
self.videoContainerView.addSubview(self.moviePlayer!.view)
self.moviePlayer!.isFullscreen = false
self.moviePlayer!.controlStyle = MPMovieControlStyle.embedded
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
}
}
override func viewDidDisappear(_ animated: Bool) {
// Stop listening notification
NotificationCenter.default.removeObserver(self, name: notificationName, object: nil);
}
I see your problem now. You're posting a "moviePlayerDoneButtonClicked" notification with no object and no userInfo dictionary.
Why are you doing it this way? The user is the one who clicks the "done" button and then iOS / movie player creates the "done" notification for your app to handle.
Remove the NotificationCenter.default.post(name: self.notificationName, ... line up there.
And change your code to this:
//Event for moviePlayer Done Button Pressed (method for notification)
func moviePlayerDoneButtonClicked(notification: NSNotification) {
print ("delegate called....")
if let userInfo = notification.userInfo
{
let finishReason = MPMovieFinishReason(rawValue: (notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] as! NSNumber).intValue)
if (finishReason == MPMovieFinishReason.userExited)
{
print ("Done button clicked.")
self.delegate.exitVideoPlayer()
}
} else {
print("I shouldn't be manually posting a moviePlayerDoneButtonClicked notification without a userInfo dictionary")
}
}

Close AVPlayer when movie is complete

I am making a simple iPad app to play a movie when a button is pressed. The movie plays and when the movie is finished I want to close AVPlayerView so it goes back to the main screen.
Currently when the video finishes it stays on the last frame.
My ViewController.Swift at the moment.
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
//MARK : Properties
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
//MARK: Actions
#IBAction func playButton(_ sender: AnyObject) {
let movieURL = Bundle.main.url(forResource: "ElephantSeals", withExtension: "mov")!
let player = AVPlayer(url: movieURL as URL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
// player.actionAtItemEnd = playerViewController.dismiss(animated: true)
}
}
As you can see, I think there might be something in actionAtItemEnd, but I'm not sure how to implement it.
Thank you.
This is working code in swift 5.3 and iOS 14.2, try this and let me know...:)
import UIKit
import AVKit
import AVFoundation
class ViewController: UIViewController {
let playerViewController = AVPlayerViewController()
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func playButton(_ sender: AnyObject) {
let movieURL = Bundle.main.url(forResource: "ElephantSeals", withExtension: "mp4")!
let player = AVPlayer(url: movieURL as URL)
playerViewController.player = player
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerViewController.player?.currentItem)
self.present(playerViewController, animated: true) {
self.playerViewController.player!.play()
}
}
#objc func playerDidFinishPlaying(note: NSNotification) {
self.playerViewController.dismiss(animated: true)
}
}
You can download sample project for same from here
https://github.com/deepakiosdev/AVPlayerViewControllerDemo
Swift 4
let playerController = AVPlayerViewController()
private func playVideo() {
guard let path = Bundle.main.path(forResource: "p810", ofType:"mp4") else {
debugPrint("video.m4v not found")
return
}
let player = AVPlayer(url: URL(fileURLWithPath: path))
playerController.player = player
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerController.player?.currentItem)
present(playerController, animated: true) {
player.play()
}
}
#objc func playerDidFinishPlaying(note: NSNotification) {
playerController.dismiss(animated: true, completion: nil)
}
Using NSNotificationCenter you can do this .
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.playerDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: videoPlayer!.currentItem)
#objc func playerDidFinishPlaying(note: NSNotification) {
// here you can do your dismiss controller logic
AVPlayerViewController.dismiss(animated: true)
print("Video Finished")
}
here is objective c code
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(nextVideo:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerViewController.player.currentItem];
Invoke AVPlayerViewControllerDelegate.
In viewDidLoad initialize the delegate and implement this method
func playerViewControllerDidStopPictureInPicture(AVPlayerViewController) {
AVPlayerViewController.dismiss(animated: true)
}
https://developer.apple.com/reference/avkit/avplayerviewcontrollerdelegate

Swift AVPlayerItem close when finished

I'm extremely new to Swift and iOS development, so please forgive my ignorance.
I'm trying to have the AVPlayer close automatically when the video is done playing. I've thought to attach the "playerDidFinishPlaying" listener to receive the notification, but once I have it, I can't find the method/event to close the Controller. I'm looking to mimic the action of clicking the "Done" button.
Here is a small snippet of code. Hopefully this is enough information. If not, I can provide further info
let destination = segue.destinationViewController as! AVPlayerViewController
let url = NSURL(string: "video url")
destination.player = AVPlayer(URL: url!)
destination.player?.play()
I've added the following notification, but again, I'm not sure what to do with it once I have it...
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidFinishPlaying:",
name: AVPlayerItemDidPlayToEndTimeNotification,
object: destination.player!.currentItem)
func playerDidFinishPlaying(note:NSNotification){
print("finished")
// close window/controller
}
Lastly, I know I'll need to remove the observer, but I'm not sure when or where to do so. Any help is greatly appreciated.
In order to "close" the controller, you should call dismissViewControllerAnimated(true, completion: nil)
So the code will look like:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidFinishPlaying:",
name: AVPlayerItemDidPlayToEndTimeNotification,
object: destination.player!.currentItem)
func playerDidFinishPlaying(note:NSNotification){
print("finished")
dismissViewControllerAnimated(true, completion: nil)
}
if your viewController is inside of a UINavigationController stack, you can also do:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "playerDidFinishPlaying:",
name: AVPlayerItemDidPlayToEndTimeNotification,
object: destination.player!.currentItem)
func playerDidFinishPlaying(note:NSNotification){
print("finished")
navigationController?.popViewControllerAnimated(true)
}
And for remove the observer, you can do within deinit{}:
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
for #iOS 11 and swift 4.2 dismiss controller are not work so just add this key in your player setup code
if #available(iOS 11.0, *) {
self.playerVC?.exitsFullScreenWhenPlaybackEnds = true
}
if you wan to allow #iOS 10 as well then write this line.
if #available(iOS 11.0, *) {
self.playerVC?.exitsFullScreenWhenPlaybackEnds = true
}
NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachEnd(notification:)), name: .AVPlayerItemDidPlayToEndTime, object:self.playerVC?.player!.currentItem)
func playerItemDidReachEnd(note:NSNotification){
print("finished")
dismissViewControllerAnimated(true, completion: nil)
}
Update for SWIFT 3:
Notes: The viewController where you are playing the audio needs: AVAudioPlayerDelegate
Also you dont need an observer
class myViewController: UIViewController, AVAudioPlayerDelegate {
var audioplayer = AVAudioPlayer()
override func viewDidAppear(_ animated: Bool) {
if soundsON{
let myFilePathString = Bundle.main.path(forResource: "backgroundSound", ofType: "mp3")
if let myFilePathString = myFilePathString
{
let myFilePathURL = URL(fileURLWithPath: myFilePathString)
do{
try audioplayer = AVAudioPlayer(contentsOf: myFilePathURL)
audioplayer.delegate = self
audioplayer.prepareToPlay()
audioplayer.play()
}catch{
print("error playing coin sound")
}
}
}
}
In this example the sound will play at viewDidAppear, and when it finishes it will call: audioPlayerDidFinishPlaying:
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
//Check if the sound that finishes comes from a certain AVAudioPlayer
if player == audioplayer{
print("The sound from -audioplayer- has finished")
// If you need to dismiss the VC:
dismiss(animated: true, completion: nil)
}
}

How to detect when AVPlayer video ends playing?

I'am using AVPlayer for playing local video file (mp4) in Swift.
Does anyone know how to detect when video finish with playing?
Thanks
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
)
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")
}
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)
}
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")
}
}
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")
}
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)
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!")
}
SWIFT 5 Update
The observer method with #objc function is not native. It is better to use event publisher in swift 5. Very simple.
Declare the following in the struct:
var pub = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
Then on any view, add
.onReceive(pub) { (output) in
print("Video Finished")
}
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
}
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")
}
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
}
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
}
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)
}
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)
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: nil, queue: .main) { noti in
guard let item = noti.object as? AVPlayerItem else{
return
}
//DidPlayToEndTime
}
I had an issue with the Notification never getting called, setting the notification inside the presentation of the AVPlayerViewController solved it for me:
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)
}
}
}
Another solution:
player.observe(\AVPlayer.actionAtItemEnd) { player, _ in
print("video did end")
}

Resources