How to do something when AVQueuePlayer finishes the last playeritem

﹥>﹥吖頭↗ 提交于 2019-12-07 03:37:30

问题


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?


回答1:


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:

AVPlayerItem *thePlayerItemA = [[AVPlayerItem alloc] initWithURL:urlA];
AVPlayerItem *thePlayerItemB = [[AVPlayerItem alloc] initWithURL:urlB];
AVPlayerItem *thePlayerItemC = [[AVPlayerItem alloc] initWithURL:urlC];    
AVPlayerItem *thePlayerItemD = [[AVPlayerItem alloc] initWithURL:urlD];  

NSArray *theItems = [NSArray arrayWithObjects:thePlayerItemA, thePlayerItemB, thePlayerItemC, thePlayerItemD, nil];
theQueuePlayer = [AVQueuePlayer queuePlayerWithItems:theItems];

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

After which I have implement the 'playerItemDidReachEnd' selector like this:

- (void)playerItemDidReachEnd:(NSNotification *)notification {
    // Do stuff here
    NSLog(@"IT REACHED THE END");
}

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.




回答2:


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 your AVQueuePlayer instance.

AVQueuePlayer* player = [AVQueuePlayer queuePlayerWithItems:currentPlaybackQueue];
[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playerItemDidReachEnd:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:currentPlaybackQueue.lastObject];

(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: using notification.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] in observeValueForKeyPath:.

[player addObserver:self forKeyPath:@"currentItem" options:NSKeyValueObservingOptionNew context:nil];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"currentItem"]) {

        if (change[NSKeyValueChangeNewKey] == [NSNull null]) {
            //last item finished playing - do something

        }
    }
}

normally for AVQueuePlayer you will register for AVPlayerItemDidPlayToEndTimeNotification for each item in playbackQueue, so unless you don't want to store playbackQueue locally and check its lastObject with notification.object in -playerItemDidReachEnd:, you would use the second way




回答3:


You likely didn't implement the -observeValueForKeyPath:ofObject:change:context: method in your class. Check out the Apple KVO guide for further details.



来源:https://stackoverflow.com/questions/12187686/how-to-do-something-when-avqueueplayer-finishes-the-last-playeritem

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!