6

我有一个 AVQueuePlayer,它是从 4 个 AVPlayerItems 的数组中创建的,并且一切正常。

我想在队列中的最后一个项目播放完毕时做点什么,我在这里看了很多答案,这是我想要的最有希望的答案: 声音完成后执行代码的最佳方法玩

在我的按钮处理程序中,我有以下代码:

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;

}

我究竟做错了什么?

4

3 回答 3

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 文件排队,然后当最后一段音频完成时,它会调用选择器,并且我的消息会出现在控制台中。

我希望这对其他人有用。

于 2012-08-30T21:36:58.073 回答
3

观察状态不会告诉您项目何时完成播放(它只会告诉您项目何时准备播放)

您所要做的就是为AVPlayerItemDidPlayToEndTimeNotification 您的playbackQueue 中的lastItem 注册,用于初始化您的AVQueuePlayer实例。

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

(注意选择器有playerItemDidReachEnd:,你忘了两个点)

所以 then-playerItemDidReachEnd:只会在你需要的时候被调用一次。(也不要忘记将自己移除为观察者 - 你可以在-playerItemDidReachEnd:using中做到这一点notification.object

不得不说,还有其他方法可以做到这一点:对 currentItem 使用 key-value 观察,然后检查 currentItem 是否更改为[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

        }
    }
}

通常,AVQueuePlayer您将为AVPlayerItemDidPlayToEndTimeNotificationin 中的每个项目注册playbackQueue,因此除非您不想在本地存储并使用inplaybackQueue检查其 lastObject,否则您将使用第二种方式notification.object-playerItemDidReachEnd:

于 2014-07-29T11:53:25.700 回答
2

您可能没有-observeValueForKeyPath:ofObject:change:context:在您的类中实现该方法。查看Apple KVO 指南以获取更多详细信息。

于 2012-08-30T03:41:22.010 回答