【问题标题】:AVPlayerItemDidPlayToEndTimeNotification only works if forcedAVPlayerItemDidPlayToEndTimeNotification 仅在强制时有效
【发布时间】:2015-03-03 00:55:22
【问题描述】:

我到处搜索,但仍然找不到解决此问题的方法。我有一个 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]];

【问题讨论】:

    标签: ios avplayer nsnotification avqueueplayer avplayeritem


    【解决方案1】:

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

    我建议在你像这样初始化播放器“之后”添加观察者。

    ...
    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];
    }
    

    希望能帮助解决问题。

    【讨论】:

    • 感谢您的回复。我尝试了两种方式。问题是在这两种情况下都会发送通知。在手动发布通知的情况下,一切正常。如果在项目实际结束时发送通知,它会执行代码,但不会播放音频。但是我在播放器和播放器项上找不到任何错误。让一切顺利进行的唯一方法是按下按钮来推进播放器。
    • “此通知可能发布在与观察者注册的线程不同的线程上”我想知道所有这些发生在后台线程上是否存在问题。
    • 我会尝试在主线程上注册观察者。我应该只在主线程上注册观察者还是所有代码(advancePlayer、initializeplayeritems 等)?
    • @dj2819 在主线程上调用代码是否解决了其余问题?
    • 在主线程上调用它并使用指向 avqueueplayer 的本地指针的组合有效。但是我在某处更改了一个小代码,现在它不想再工作了。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      经过几天不同的代码组合和研究,我发现: 问题:有一个 AVQueuePlayer,一次有大约 20 多个项目,并注册了观察者。当 AVPlayerItemDidPlayToEndTimeNotification 被调用时,代码将前进到下一个项目,但该项目不会播放。 由于我的 avplayeritem 附加了观察者,因此我不能只使用 AVPlayer 的 advanctonextitem 属性。我需要使用 AVPlayerItemDidPlayToEndTimeNotification。现在,当调用此方法时,它可以正常工作,但是当 AVPlayerItem 发布通知时,它将在队列中的下一个 AVPlayerItem [templayer advanceToNextItem] 处调用,其数据将丢失。否则,如果我通过自己的代码发布通知,它将完美地推进到下一个项目。为了解决这个问题,我尝试了一切,但这就是我最终的结果,它运行良好,只是我需要它,所以如果其他人被卡住了,它会帮助他们。我使用一个可变数组来存储“队列”并且一次只加载一个 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];
          }
      });
      }
      

      【讨论】:

        【解决方案4】:

        1 - 我会很惊讶 PlayerItems 丢失了。它们被玩家保留在某个地方,在一个数组中,......

        2 - Appledoc 明确声明您可以在任何线程上发送通知。

        在这种情况下,一切都应该工作,但它没有,我建议检查上下文。我经历过的一个简单案例(我已经浪费了几个小时!!)是持有玩家的视图被处理掉了。在这种情况下,视频仍在播放。所以很难调试。

        我终于放弃了使用通知系统,转而使用 KVO。 ( [玩家 addObserver:self keypath:@"status" ...)

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

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-09-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多