5

Problem : In collectionview cell which has player

if I play two Video simultaneously and seek first Video to end then AVPlayerItemDidPlayToEndTime fired for two times and both videos restarted

In collection view cell I have

override func awakeFromNib() {
        NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem, queue: .main, using: {[weak self]  (notification) in
            if self?.player != nil {
                self?.player?.seek(to: kCMTimeZero)
                self?.player?.play()
            }
        })
   }

and one play button action which play the video.
In cell I have slider to seek.

enter image description here

Any Help would be appreciated

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98
  • Should you remove observer for cell reusability? – Sandeep Mar 21 '18 at 10:21
  • I didn't do that. the gif is for first two cells I didn't scroll the page. @Sandeep – Prashant Tukadiya Mar 21 '18 at 10:22
  • I am initiating avplayer on button action is it a problem ? @Sandeep – Prashant Tukadiya Mar 21 '18 at 10:24
  • I dont think that is problem. You would initialize it anyway on some user action. Do you really need to be able to play both videos at the same time ? – Sandeep Mar 21 '18 at 10:38
  • @Sandeep Thanks for clear this. Yes i wan to play two videos at same time. and if you observe seconds for that I have used `addPeriodicTimeObserver` which shows correct time for both videos. while Notification not work for two instances Strange !! – Prashant Tukadiya Mar 21 '18 at 10:44
  • 1
    @Sandeep Solution in my mind is to check time in closure if it is not at end then i will not reset the time to 0 – Prashant Tukadiya Mar 21 '18 at 10:45
  • I am not sure if that is correct way to handle it. Handling it in notification seem like the way to go. But, you can always try if that solves your issue. – Sandeep Mar 21 '18 at 10:51
  • @Sandeep Fixed with Dan's Answer. should I file a bug to apple ? – Prashant Tukadiya Mar 21 '18 at 11:30
  • As the answer says, you were observing optional value which could have been nil and fired notification for all the items. I think that is expected behavior and not bug – Sandeep Mar 21 '18 at 11:32
  • @Sandeep Okay thanks but Don't you think observing nil should not conflict with other's ? – Prashant Tukadiya Mar 21 '18 at 11:35
  • Please look at the documentation https://developer.apple.com/documentation/foundation/nsnotificationcenter/1415360-addobserver?language=swift. You can see object is optional type, if you pass object to it you receive the notification for that specific object while if you pass nil you get notifications from all the objects. – Sandeep Mar 21 '18 at 11:39

2 Answers2

8

Make sure that player and player?.currentItem are not equal to nil when you're registering for notifications. To me, it seems like one of them was nil and you're basically subscribing to all of the .AVPlayerItemDidPlayToEndTime notifications (since object is nil).

To avoid that, subscribe to the notifications right after assigning AVAsset to the player.

Dan Karbayev
  • 2,870
  • 2
  • 19
  • 28
  • Thank You so much . Yes it is the issue. If AVPlayer Item is nil then notification will fire for all AVPlayer !! – Prashant Tukadiya Mar 21 '18 at 11:28
  • Some good code on this implementation here https://stackoverflow.com/a/36911168/480415 – RyanG Nov 21 '19 at 20:18
  • @RyanG thanks, but the link you shared is for passing data in userInfo. That's not the case here. Here we need to pass the `player.currentItem` to the object parameter, in order to only subscribe for notifications from that item. – nevos Nov 28 '19 at 11:40
  • @nevos Correct, but it gives the basis on how to set it up. I posted an answer with more specific code on how to handle – RyanG Dec 05 '19 at 16:47
1

Swift 5.1

Pass the item as your object:

// Stored property
let player = AVPlayer(url: videoUrl)

// When you are adding your video layer
let playerLayer = AVPlayerLayer(player: player)
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: player.currentItem)

// add layer to view 

Then when you get that notification, here's how you can get the currentItem:

// Grab the item from the notification object and ensure its the same item as the current players item
if let item = notification.object as? AVPlayerItem,
    let currentItem = player.currentItem,
    item == currentItem {

    NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: nil)

    // remove player from view or do whatever you need to do here
}     
RyanG
  • 4,393
  • 2
  • 39
  • 64