AVPlayerItemDidPlayToEndTimeNotification仅在强制时有效

时间:2015-03-03 00:55:22

标签: ios avplayer nsnotification avqueueplayer avplayeritem

我到处搜寻,但仍无法找到解决此问题的方法。我有一个AVQueuePlayer用于我正在制作的广播应用。它一次有大约20 AVPlayerItems排队。我将AVPlayerItemDidPlayToEndTimeNotification观察者添加到每个项目中。通知被触发并执行代码以删除元数据和状态的关键观察者,因此它不会崩溃并前进到队列中的下一个项目。但是它并不想玩。没有错误,状态已准备就绪,AVPlayer URL已完全加载。如果我单击按钮来调用advancenextitem它可以很好地工作并且也可以完美地播放。 现在最奇怪的是:如果我手动发布通知,通知代码就能完美运行。我会感激任何帮助或输入,因为我一直在努力让这一切发挥作用。

if (playlist != nil) {
    playlistInterval = [playlist count]/4.0;
    NSMutableArray *playerItems = [[NSMutableArray alloc] initWithCapacity:playlistInterval];

        for(int i = 0; i < playlistInterval; i++) {
            NSString *tempString = [playlist objectAtIndex:i];
            //NSLog(@"%@", tempString);
            NSURL *temp = [[NSURL alloc] initWithString:tempString];
            AVPlayerItem *itemTemp = [AVPlayerItem playerItemWithURL:temp];
            [itemTemp addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
            [itemTemp addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
            [[NSNotificationCenter defaultCenter] addObserver: self
                                                     selector: @selector(playerItemDidReachEnd:)
                                                         name: AVPlayerItemDidPlayToEndTimeNotification
                                                       object: itemTemp];
            [playerItems addObject:itemTemp];
            playlistCounter++;
        }
        qPlayer = [AVQueuePlayer queuePlayerWithItems:playerItems];

        qPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
        [   qPlayer addObserver:self
                     forKeyPath:@"rate"
                        options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                        context:rateDidChangeKVO];

        [qPlayer addObserver:self forKeyPath:@"currentItem.duration"
                     options:0
                     context:durationDidChangeKVO];
    return YES;
}
return NO;

}

- (IBAction)advancePlayer:(id)sender {
    unsigned long size = [[qPlayer items] count];
    NSLog(@"%tu",size);
    if (size <= 0) {
        [self initializePlayerWithItems:currentKey];
        [qPlayer play];
    } else {
        //NSLog(@"%@",[qPlayer currentItem]);
        [[qPlayer currentItem] removeObserver:self forKeyPath:@"status"];
        [[qPlayer currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
        [qPlayer advanceToNextItem];
        [qPlayer play];
    }

}

- (void)playerItemDidReachEnd:(NSNotification *)notification {
[self advancePlayer:nil];
NSLog(@"IT REACHED THE END");

}

现在,如果我打电话给这一切,一切都可以通过按钮或其他东西完美地完成:

[[NSNotificationCenter defaultCenter]
 postNotificationName:@"AVPlayerItemDidPlayToEndTimeNotification"
 object:[qPlayer currentItem]];

4 个答案:

答案 0 :(得分:2)

我没有测试这个理论的测试项目,但我猜测通知会丢失,因为它们不是播放器的当前项目,只是添加观察者时的局部变量。

我建议您在启动播放器之后“添加”观察者。

...
qPlayer = [AVQueuePlayer queuePlayerWithItems:playerItems];

qPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;

[[NSNotificationCenter defaultCenter] addObserver: self
                                         selector: @selector(playerItemDidReachEnd:)
                                             name: AVPlayerItemDidPlayToEndTimeNotification
                                           object: [qPlayer currentItem]];

然后在推进播放器后,您可以再次添加观察者以获取下一个当前项目。

- (IBAction)advancePlayer:(id)sender {
    unsigned long size = [[qPlayer items] count];
    NSLog(@"%tu",size);
    if (size <= 0) {
        [self initializePlayerWithItems:currentKey];
        [qPlayer play];
    } else {
        //NSLog(@"%@",[qPlayer currentItem]);
        [[qPlayer currentItem] removeObserver:self forKeyPath:@"status"];
        [[qPlayer currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
        [qPlayer advanceToNextItem];

        [[NSNotificationCenter defaultCenter] addObserver: self
                                         selector: @selector(playerItemDidReachEnd:)
                                             name: AVPlayerItemDidPlayToEndTimeNotification
                                           object: [qPlayer currentItem]];
        [qPlayer play];
}

希望这有助于解决问题。

答案 1 :(得分:1)

确保您在主线程上注册通知。

dispatch_async(dispatch_get_main_queue(), ^{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(itemDidFinishPlaying:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:self.playerItem];

});

答案 2 :(得分:0)

经过几天不同的代码和研究组合,这就是我发现的: 问题:拥有注册观察员的AVQueuePlayer一次约有20多个项目。当调用AVPlayerItemDidPlayToEndTimeNotification时,代码将前进到下一个项目,但该项目不会播放。 由于我有观察员附加到我的avplayeritems我不能只使用AVPlayer的advanctonextitem属性。我需要使用AVPlayerItemDidPlayToEndTimeNotification。现在当这个方法被调用时它工作得很好但是当AVPlayerItem发布通知时,它将在[templayer advanceToNextItem]调用,队列中的下一个AVPlayerItem,它的数据将丢失。否则,如果我通过我自己的代码发布通知,它将完美地前进到下一个项目。为了解决这个问题,我尝试了所有的东西,但这是我最终完成的地方,它完美无缺,只是想要我需要,所以也许如果其他人被卡住它会帮助他们。我使用了一个可变数组来存储&#34;队列&#34;并且一次只加载一个AVPlayerItem。

if (playlist != nil) {
    playlistInterval = [playlist count]/4.0;
   //Array to hold AVPlayerItems
    _playerItems = [[NSMutableArray alloc] initWithCapacity:playlistInterval];
    for(int i = 0; i < playlistInterval; i++) {
        AVPlayerItem *itemTemp = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:[playlist objectAtIndex:playlistCounter]]];
        [itemTemp addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
        [itemTemp addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
        [[NSNotificationCenter defaultCenter] addObserver: self
                                                 selector: @selector(playerItemDidReachEnd:)
                                                     name: AVPlayerItemDidPlayToEndTimeNotification
                                                   object: itemTemp];
        [_playerItems addObject:itemTemp];
        playlistCounter++;
    }
   //Init player to only one item
    self.player = [[AVPlayer alloc] initWithPlayerItem:[_playerItems objectAtIndex:0]];
   //Don't do anything since will be handling advancing
    self.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
    [   self.player addObserver:self
                      forKeyPath:@"rate"
                         options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                         context:rateDidChangeKVO];

    [self.player addObserver:self forKeyPath:@"currentItem.duration"
                      options:0
                      context:durationDidChangeKVO];
    return YES;
}

    - (void)playerItemDidReachEnd:(NSNotification *)notification {
//NSLog(@"notifiacation thread: %d", [NSThread isMainThread]);
if ([currentKey isEqualToString:@"morning slokha"] || selection != 0)
    return;
dispatch_async(dispatch_get_main_queue(), ^{
    unsigned long size = [_playerItems count];
    NSLog(@"%tu",size);
    [self resetSlider];
    if (_playerItems.count <= 1) {
        [self initializePlayerWithItems:currentKey];
        [self.player play];
    } else {
        //NSLog(@"%@",[qPlayer currentItem]);
        @try{
            [[_playerItems objectAtIndex:0] removeObserver:self forKeyPath:@"status"];
            [[_playerItems objectAtIndex:0] removeObserver:self forKeyPath:@"timedMetadata"];
            [[self.player currentItem] removeObserver:self forKeyPath:@"status"];
            [[self.player currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
        }@catch(id anException){
            //do nothing, obviously it wasn't attached because an exception was thrown
        }
        [[NSNotificationCenter defaultCenter] addObserver: self
                                                 selector: @selector(playerItemDidReachEnd:)
                                                     name: AVPlayerItemDidPlayToEndTimeNotification
                                                   object: [_playerItems objectAtIndex:0]];
        [[NSNotificationCenter defaultCenter] addObserver: self
                                                 selector: @selector(playerItemDidReachEnd:)
                                                     name: AVPlayerItemDidPlayToEndTimeNotification
                                                   object: [self.player currentItem]];
        [_playerItems removeObjectAtIndex:0];
        //Replace AVPlayerItem manually
        [self.player replaceCurrentItemWithPlayerItem:[_playerItems objectAtIndex:0]];
        [self.player seekToTime:kCMTimeZero];
        [self.player play];
    }
});
}

答案 3 :(得分:0)

1 - 我会非常惊讶PlayerItems迷路了。它们由玩家保留在某个阵列的某个地方......

2 - Appledoc明确表示您可以在任何主题上发送通知。

在这种情况下,一切都应该有效,但事实并非如此,我建议检查上下文。我经历过的一个简单的案例(我已经失去了几个小时!!)是持有玩家的视图被处理掉了。在这种情况下,视频仍在播放。因此很难调试。

我终于放弃使用通知系统,切换到KVO。 ([player addObserver:self keypath:@“status”...)

我在处理视图时遇到异常,因为视图仍然被注册为玩家观察者。

我已经解决了视图错误,然后我的视图按预期收到了playerItem的通知。

通常是这样的。 Apple告诉我们这很简单。 如果它不起作用,我们会做很多变通办法和个人补丁,而不是试图理解我们所做的那些会产生副作用的事情......