I've got an AVQueuePlayer which I'm creating from an array of 4 AVPlayerItems, and it all plays fine.
I want to do something when the last item in the queue finishes playing, I've looked a load of answers on here and this is the one that looks most promising for what I want: The best way to execute code AFTER a sound has finished playing
In my button handler i have this code:
static const NSString *ItemStatusContext;
[thePlayerItemA addObserver:self forKeyPath:@"status" options:0 context:&ItemStatusContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd)
name:AVPlayerItemDidPlayToEndTimeNotification
object:thePlayerItemA];
theQueuePlayer = [AVQueuePlayer playerWithPlayerItem:thePlayerItemA];
[theQueuePlayer play];
and then I have a function to handle playerItemDidReachEnd:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
// Do stuff here
NSLog(@"IT REACHED THE END");
}
But when I run this I get an Internal Inconsistency Exception:
An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: status
Observed object: <AVPlayerItem: 0x735a130, asset = <AVURLAsset: 0x73559c0, URL = file://localhost/Users/mike/Library/Application%20Support/iPhone%20Simulator/5.0/Applications/A0DBEC13-2DA6-4887-B29D-B43A78E173B8/Phonics%2001.app/yes.mp3>>
Change: {
kind = 1;
}
What am I doing wrong?
You likely didn't implement the
-observeValueForKeyPath:ofObject:change:context:
method in your class. Check out the Apple KVO guide for further details.This approach seems to work for me, within my button handler i've got a bunch of stuff to create URLs for the 4 mp3 files I want to play, then:
After which I have implement the 'playerItemDidReachEnd' selector like this:
This queues up the 4 MP3 files and then when the last piece of audio has finished it calls the selector and my message appears in the console.
I hope that this is useful for someone else.
Observing for status will not tell you when item is finished playing(it only tells when item is readyToPlay)
All you have to do it is to register for
AVPlayerItemDidPlayToEndTimeNotification
for the lastItem in your playbackQueue, which use to initialise yourAVQueuePlayer
instance.(note that selector has
playerItemDidReachEnd:
, you forgot two dots)So then
-playerItemDidReachEnd:
will only be called exactly once in the end as you need it. (also don't forget to remove yourself as an observer - you can do it in-playerItemDidReachEnd:
usingnotification.object
)Have to say that there is other way to do this: Using key-value observing for currentItem, then checking if the currentItem changed to
[NSNull null]
inobserveValueForKeyPath:
.normally for
AVQueuePlayer
you will register forAVPlayerItemDidPlayToEndTimeNotification
for each item inplaybackQueue
, so unless you don't want to storeplaybackQueue
locally and check its lastObject withnotification.object
in-playerItemDidReachEnd:
, you would use the second way