14
votes

When using AVPlayer to play audio from an url it will discontinue to play when for example disconnecting from wifi.

[player play];

Does not resume the AVPlayer

player.rate // Value is 1.0

player.currentItem.isPlaybackLikelyToKeepUp // Value is YES

player.status // Value is AVPlayerStatusReadyToPlay

player.error // Value is nil

But the player is not playing any audio.

How do I handle a disconnect from AVPlayer, for reconnecting the AVPlayer and start playing again?

3
Not sure about when playing only audio, but for video, listen for AVPlayerItemFailedToPlayToEndTimeNotification as suggested in the other answers. Also it seems that for HLS live video streams, we need to recreate the AVPlayerItem and set it to the AVPlayer. Just calling play(), doing seeking etc does not seem to work for us. Looking for a better solution right now but this is the best I know at this point. Also if the avplayeritem has .status failed, we also need to recreate it (my assumption). - Jonny

3 Answers

13
votes

In order to handle network changes, you have to add an observer for AVPlayerItemFailedToPlayToEndTimeNotification.

- (void) playURL:(NSURL *)url
{
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemFailedToPlayToEndTime:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:playerItem];
    self.player = [AVPlayer playerWithPlayerItem:playerItem];
    [self.player play];
}

- (void) playerItemFailedToPlayToEndTime:(NSNotification *)notification
{
    NSError *error = notification.userInfo[AVPlayerItemFailedToPlayToEndTimeErrorKey];
    // Handle error ...
}
4
votes

You should add observer for AVPlayerItemPlaybackStalledNotification.

AVPlayerItemFailedToPlayToEndTimeNotification has no value for me on this problem.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackStalled:) name:AVPlayerItemPlaybackStalledNotification object:trackItem];

As the doc says, file-based playback does not continue if necessary streaming media wasn’t delivered in a timely fashion over a network.

The notification’s object is the AVPlayerItem instance whose playback was unable to continue because the necessary streaming media wasn’t delivered in a timely fashion over a network. Playback of streaming media continues once a sufficient amount of data is delivered. File-based playback does not continue.

This explained why AVPlayer can resume HLS streams after network switch, but cannot do the same if I use AVPlayer to play TuneIn resources which is file-based.

Then the answer becomes simple.

- (void)playbackStalled:(NSNotification *)notification {
    if ([self isFileBased:streamUri]) {
        // Restart playback
        NSURL *url = [NSURL URLWithString:streamUri];
        AVPlayerItem *trackItem = [AVPlayerItem playerItemWithURL:url];
        AVPlayer *mediaPlayer = [AVPlayer playerWithPlayerItem:trackItem];
        [self registerObservers:trackItem player:mediaPlayer];
        [mediaPlayer play];
    }
}

Further reading on discussion of automaticallyWaitsToMinimizeStalling.

1
votes

0xced's answer for Swift 3/4:

var playerItem: AVPlayerItem?
var player: AVPlayer?

func instantiatePlayer(_ url: URL) {
    self.playerItem = AVPlayerItem(url: url)            
    self.player = AVPlayer(playerItem: self.playerItem)
        NotificationCenter.default.addObserver(self, selector: #selector(playerItemFailedToPlay(_:)), name: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, object: nil)
}

func playerItemFailedToPlay(_ notification: Notification) {
    let error = notification.userInfo?[AVPlayerItemFailedToPlayToEndTimeErrorKey] as? Error

}