AVQueuePlayer AVPlayerItemDidPlayToEndTimeNotification fails to call

2.7k views Asked by At

Im using AVQueuePlayer to loop through an array of AVPlayerItems. The way I'm looping it, I listen to the AVPlayerItemDidPlayToEndTimeNotification and every time its called, I add the current object to the end of the queue. heres the code:

    viewWillAppear
{
    ...

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

        [_queuePlayer play];
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
    AVPlayerItem *p = [notification object];
    AVPlayerItem *fakeNew = [[AVPlayerItem alloc] initWithAsset:p.asset];
    if (_queuePlayer.items.count == 1)
    {
        [p seekToTime:kCMTimeZero];
        [_queuePlayer play];
    }
    else
    {
        [_queuePlayer insertItem:fakeNew afterItem:[_queuePlayer.items lastObject]];
    }
    NSLog(@"array of items to play:%lu", (unsigned long)_queuePlayer.items.count);

}

The problem is, that the method is called only for the first video that plays, after that, the method stops getting called, so if for example i have 2 movies in the array, it would play them both+the first one again, any idea why is this happening?

More Info: also tried to make a new player every time and set it to layer. failed to send the notification more than once just the same

- (void)playerItemDidReachEnd:(NSNotification *)notification {
    AVPlayerItem *p = [notification object];
    [self.playList removeObjectAtIndex:0];
    [self.playList addObject:p];
    AVPlayer *newPlayer = [[AVPlayer alloc] initWithPlayerItem:[self.playList objectAtIndex:0]];
    _player = newPlayer;
    self.AVPlayerLayerView.layer.player = self.player;
    [_player play];

}
2

There are 2 answers

1
Yoav Schwartz On BEST ANSWER

After a lot of messing around, apparently for whatever reason, the view unregistered as observer every time, I just removed and added observer after every notification:

- (void)playerItemDidReachEnd:(NSNotification *)notification {
    AVPlayerItem *p = [notification object];
    AVPlayerItem *fakeNewItem = [[AVPlayerItem alloc] initWithAsset:p.asset];
    [self.playList removeObjectAtIndex:0];
    [self.playList addObject:fakeNewItem];
    AVPlayer *newPlayer = [[AVPlayer alloc] initWithPlayerItem:[self.playList objectAtIndex:0]];
    _player = newPlayer;
    self.AVPlayerLayerView.layer.player = self.player;
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(playerItemDidReachEnd:)
                                                 name:AVPlayerItemDidPlayToEndTimeNotification
                                               object:[_player currentItem]];
    [_player play];

}
0
Ricowere On

For a clean approach to resolve this issue. I approached with the next piece of code instead

The first is you have to add the code necessary to receive a feedback from the AVPlayer when the reproduction time changes.

- (void)addPeriodicTimeObserverForReproductionChanges {
    @weakify(self);
    [self.player 
    addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(kBQDefaultTimeIntervalReproductionChanges, NSEC_PER_SEC) 
    queue:self.eventsQueue
    usingBlock:^(CMTime time) {
    @strongify(self);
    [self dispatchBlockOnMainQueue:^{
        if ([self.delegate respondsToSelector:@selector(playerItemController:didChangeReproductionTime:)])
             [self.delegate playerItemController:self
                       didChangeReproductionTime:time];

         [self checkForItemPlayToEnd];
     }]; 
 }];

}

- (void)checkForItemPlayToEnd 
{
    CMTime durationScaled = CMTimeConvertScale(self.duration,self.player.currentTime.timescale, kCMTimeRoundingMethod_Default);

    if (CMTIME_COMPARE_INLINE(durationScaled, ==, self.player.currentTime)) {
        [self playerDidFinishReproducingItem];
    }
}