I have an AVQueuePlayer with an array of video URLs that I need to play one after the other. Everything works great until I try to add an observer to track the start of each video.

To make the AVQueuePlayer auto-advance to the next video, I've got this:

[[NSNotificationCenter defaultCenter] addObserver:self           
                                         selector:@selector(playerItemDidReachEnd:) 
                                             name:AVPlayerItemDidPlayToEndTimeNotification 
                                           object:[playerItems lastObject]];


- (void)playerItemDidReachEnd:(NSNotification *)notification {
  [self nextVideoOrExit];
}

- (void)nextVideoOrExit
{
  if ([player.items count] == 1)
    [self shutdown];
  else
    [player advanceToNextItem];
}

- (void)shutdown
{
  [player removeAllItems];
  [self dismissPlayerView];
}

So far, this works as expected - the videos play one after the other. Now I also need to know immediately when a video starts to play so I can update a label in the UI with the file name.

Here's how I add AVPlayerItems and the observer used to track when the next video plays:

  for (Tag *tag in self.tagsWithVideos) {
    tagUrlToTag[tag.video.url] = tag;
    NSURL *url = [[NSURL alloc] initWithString:tag.video.url];
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:nil];
    AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:asset];
    [player insertItem:playerItem afterItem:nil];

    [playerItems addObject:playerItem];

    [playerItem addObserver:self
             forKeyPath:@"status"
                options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                context:@"AVPlayerStatus"];
    NSLog(@"LOADING ANOTHER VID");
  };

And here's how I'm handling the observer:

- (void)observeValueForKeyPath:(NSString *)path
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {

  if (context == @"AVPlayerStatus") {

    AVPlayerStatus status = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
    switch (status) {
      case AVPlayerStatusUnknown: {

      }
        break;

      case AVPlayerStatusReadyToPlay: {
        NSLog(@"AVPlayerStatusReadyToPlay");
      }
        break;
    }
  }
}

When I run this, the first video plays fine. It crashes at [player advanceToNextItem]; within nextVideoOrExit

The crash:

2015-06-07 13:31:52.017 AppName[7375:1775566] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x17001bea0 of class AVPlayerItem was deallocated while key value observers were still registered with it. Current observation info: ( Context: 0x1001da910, Property: 0x17065b4e0> )'

If I comment out the [player advanceToNextItem]; line it doesn't crash, but it also doesn't play the second video in the list.

I'm not sure where I'm supposed to remove the observer (or if there's a better way to identify when a video begins playing.

Any advice? Thanks!

1

There are 1 answers

0
Anton Chikin On BEST ANSWER

As per AVFoundation Programming Guide you can use KVO to monitor values of the player itself instead of every single item:

  • If the user uses multitasking to switch to a different application, a player’s rate property will drop to 0.0.

  • A player’s currentItem property changes as a player item is created for an HTTP live stream.

  • A player or player item’s status property may change if playback fails for some reason.