Search code examples
objective-cios5avfoundationavplayeravqueueplayer

AVQueuePlayer playing several audio tracks in background iOS5


I used AVQueuePlayer to play several items in background. And code worked perfect in iOS4. And in iOS5 AVQueuePlayer changed its behavior, so player stops playing after first item is ended.

Matt Gallagher wrote a hint in this post. "As of iOS 5, it appears that AVQueuePlayer no longer pre-buffers. It did pre-buffer the next track in iOS 4."

So my question is how to play several items in background using AVPlayer or AVQueuePlayer in iOS5.


Solution

  • Matt Gallagher's answer in his blog: "You must observe the current item in the AVQueuePlayer. When it changes, you must use UIApplication to start a backgroundTask and only end the background task when you receive a ready to play notification for the next file."

    Actually, this did not helped me.

    So my solution is:

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    

    I can't explain why, but this line of code put before adding new AVPlayerItem made my code work. Also for those one who adds next track in background and uses method

    - (void)loadValuesAsynchronouslyForKeys:(NSArray *)keys completionHandler:(void (^)(void))handler;
    

    You must add AVPlayerItem to player on the main thread. like this:

    - (void)addAsset:(AVAsset*)as
    {
        [player insertItem:[AVPlayerItem playerItemWithAsset:as] afterItem:[player currentItem]];
    }
    
    .........
    
    //adding new track 
    
    AVURLAsset* as = [[self createAsset:urlString] retain];
    NSArray *keys = [NSArray arrayWithObject:@"tracks"];
    [as loadValuesAsynchronouslyForKeys:keys completionHandler:^(void) {
    NSError *error = 
    AVKeyValueStatus trackStatus = [as statusOfValueForKey:@"tracks" error:&error];
    switch (trackStatus) {
        case AVKeyValueStatusLoaded:
                [self performSelectorOnMainThread:@selector(addAsset:) withObject:as waitUntilDone:YES];
                [as release];
        break;
        case AVKeyValueStatusFailed:
                [as release];
        break;
        case AVKeyValueStatusCancelled:
                [as release];
        break;
        default:
        break;
        }
    
    }];
    

    UPDATE: Matt Gallagher was right, but it works only if you do not use asynchronous loading.