我有一个AVQueuePlayer,我是从4个AVPlayerItem的数组创建的,它都可以正常播放。
当队列中的最后一个项目完成播放时,我想做点什么,我在这里看了很多答案,这个看起来最符合我的想法: The best way to execute code AFTER a sound has finished playing
在我的按钮处理程序中,我有以下代码:
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];
然后我有一个处理playerItemDidReachEnd的函数:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
// Do stuff here
NSLog(@"IT REACHED THE END");
}
但是当我运行这个时,我得到一个内部不一致的例外:
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;
}
我做错了什么?
答案 0 :(得分:8)
这种方法似乎对我有用,在我的按钮处理程序中我有很多东西要为我想要播放的4个mp3文件创建URL,然后:
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];
之后我实现了'playerItemDidReachEnd'选择器,如下所示:
- (void)playerItemDidReachEnd:(NSNotification *)notification {
// Do stuff here
NSLog(@"IT REACHED THE END");
}
这会将4个MP3文件排队,然后当最后一段音频完成时,它会调用选择器并在控制台中显示我的消息。
我希望这对其他人有用。
答案 1 :(得分:3)
观察状态不会告诉您项目何时完成播放(仅告知项目何时准备好播放)
您只需在playbackQueue 中为lastItem注册AVPlayerItemDidPlayToEndTimeNotification
即可用于初始化AVQueuePlayer
个实例。
AVQueuePlayer* player = [AVQueuePlayer queuePlayerWithItems:currentPlaybackQueue];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:currentPlaybackQueue.lastObject];
(请注意,选择器有playerItemDidReachEnd:
,你忘记了两个点)
那么-playerItemDidReachEnd:
只会在你需要的时候最后调用一次。
(也不要忘记以观察者的身份移除自己 - 您可以使用-playerItemDidReachEnd:
在notification.object
中执行此操作)
不得不说有其他方法:
使用currentItem的键值观察,然后检查[NSNull null]
中currentItem是否更改为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
}
}
}
通常AVQueuePlayer
您将为AVPlayerItemDidPlayToEndTimeNotification
中的每个项目注册playbackQueue
,因此,除非您不想在本地存储playbackQueue
并检查其lastObject在notification.object
中使用-playerItemDidReachEnd:
,您将使用第二种方式
答案 2 :(得分:2)
您可能没有在班级中实施-observeValueForKeyPath:ofObject:change:context:
方法。查看Apple KVO guide了解更多详情。