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!
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.