Question

I'm trying to catch a moment when AVPlayer is unable to continue playback in case no more media available (too slow network, signal loss, etc). As described in documentation and different examples I'm using KVO to detect this:

item = [[AVPlayerItem alloc] initWithURL:audioURL];
player = [AVPlayer playerWithPlayerItem:item];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onItemNotification:) name:AVPlayerItemPlaybackStalledNotification object:item];
[item addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[item addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];

...

- (void) onItemNotification:(NSNotification*)not
{
    NSLog(@"Item notification: %@", not.name);
}

...

- (void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
    NSLog(@"Observe keyPath %@", keyPath);
}

I'm starting a playback and turn WiFi off after that. Unfortunately neither 'playbackBufferEmpty' nor 'AVPlayerItemPlaybackStalledNotification' comes. At the moment when playback stops I receive only one AVPlayerItemTimeJumpedNotification and that's all. However there were at least 2 times when I got these notifications. But I can't figure out how to get them every time when playback is stalled. Am I doing something wrong?

Était-ce utile?

La solution

First try to disconnect internet from your router and you will get playbackBufferEmpty notification. To handle network switching you will need to implemented Reachability

Autres conseils

There are 2 cases where player can get stuck: it never starts or it runs out of buffered data. I use the following to handle both cases:

When you create a AVPlayer instance:

[_player addObserver:self forKeyPath:@"rate" options:0 context:nil];
[_player.currentItem addObserver:self forKeyPath:@"status" options:0 context:nil];

This is the handler:

-(void)observeValueForKeyPath:(NSString*)keyPath
                     ofObject:(id)object
                       change:(NSDictionary*)change
                      context:(void*)context {

    if ([keyPath isEqualToString:@"status"]) {
        if (_player.status == AVPlayerStatusFailed) {
            //Failed to start.
            //Description from the docs:
            //  Indicates that the player can no longer play AVPlayerItem instances because of an error. The error is described by
            //  the value of the player's error property.
        }
    } else if ([keyPath isEqualToString:@"rate"]) {
        if (_player.rate == 0 && //playback rate is 0
                CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, >, kCMTimeZero) && //video has started
                CMTIME_COMPARE_INLINE(_player.currentItem.currentTime, <, _player.currentItem.duration) && //video hasn't reached the end
                _isPlaying) { //instance variable to track playback state
            //Video stalled. Possible connection loss or connection is too slow.
        }
    }
}

Don't forget to remove observers when you are done:

[_player.currentItem removeObserver:self forKeyPath:@"status"];
[_player removeObserver:self forKeyPath:@"rate"];

See my answer here to see how I handle stalled video: AVPlayer stops playing and doesn't resume again

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top