Armands L. Armands L. - 9 months ago 104
iOS Question

AVQueuePlayer - keep active while item is being buffered

Is it possible to keep the AVQueuePlayer active while a remote AVPlayerItem is being buffered?

Currently, when item goes into buffering, playback stops and all info set by MPNowPlayingInfoCenter is removed - lock screen, command center, external device etc.

I've been testing the AVPlayer/AVQueuePlayer for some time now, and seems like the only option is to:

  1. Observe remote AVPlayerItem for playbackBufferEmpty

  2. When observer triggers, insert new AVPlayerItem with empty mp3 file.

  3. Keep observing remote item's buffer and once it hits playbackLikelyToKeepUp, swap the items in place and advance to the remote one.

The problem with this method is, that once you advance to the empty item, AVQueuePlayer stops buffering the remote.
I've set a strong reference to both items and actionAtItemEnd is set to AVPlayerActionAtItemEndNone.
Network is monitored by Charles.

Is there any other methods to achieve this?


I managed to keep the playback active using two AVPlayer instances.

First instance holds the remote AVPlayerItem and second empty sound file, just few ms long. You can seek empty item to the end when loaded.

When remote item goes into buffering, pause it and play empty file. As it's already at the end, it will hold the playback session active. Just set the actionAtItemEnd to AVPlayerActionAtItemEndNone.

Even though this works, you can expierence some wierd behavior when remote item's buffer changes to playbackLikelyToKeepUp and you swap the players. Remote item can hop back to playbackBufferEmpty and the player swap logic can stuck into never-ending loop.

I solved this by delaying buffer update:

-(void)setPendingBuffer:(PlayerItemBuffer)pendingBuffer {

    _pendingBuffer = pendingBuffer;

    if (self.bufferDispatchTimer.valid) {
        [self.bufferDispatchTimer invalidate];
    self.bufferDispatchTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(bufferDispatchTimerDidFire) userInfo:nil repeats:NO];