Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

start playing audio from a background task via AVAudioPlayer in Xcode

I am trying to start playing a sound from a background task via an AVAudioPlayer that is instantiated then, so here's what I've got.

For readability I cut out all user selected values.

- (void)restartHandler {
    bg = 0;
    bg = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:bg];
    }];
    tim = [NSTimer timerWithTimeInterval:.1 target:self selector:@selector(upd) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:tim forMode:NSRunLoopCommonModes];
}

- (void)upd {
    if ([[NSDate date] timeIntervalSinceDate:startDate] >= difference) {
        [self playSoundFile];
        [tim invalidate];
        [[UIApplication sharedApplication] endBackgroundTask:bg];
    }
}

- (void)playSoundFile {

    NSError *sessionError = nil;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&sessionError];


    // new player object
    _player = [[AVQueuePlayer alloc] init];
    [_player insertItem:[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:@"Manamana" withExtension:@"m4a"]] afterItem:nil];

    [_player setVolume:.7];
    [_player play];

    NSLog(@"Testing");
}

Explanation: bg, tim, startDate, difference and _player are globally declared variables. I call - (void)restartHandler from a user-fired method and inside it start a 10Hz repeating timer for - (void)upd. When a pre-set difference is reached, - (void)playSoundFile gets called and initiates and starts the player. The testing NSLog at the end of the method gets called.

Now the strange thing is if I call - (void)playSoundFile when the app is active, everything works out just fine, but if I start it from the background task it just won't play any sound.

Edit

So I tried using different threads at runtime and as it seems, if the Player is not instantiated on the Main Thread this same problem will appear. I also tried using AVPlayer and AVAudioPlayer where the AVAudioPlayer's .playing property did return YES and still no sound was playing.

like image 811
Finn Gaida Avatar asked Dec 06 '13 10:12

Finn Gaida


1 Answers

From what I've learned after writing an player App, it seems that you can not start playing audio when your App is already in background for longer than X seconds, even if you have configured everything right.

To fix it, you have to use background task wisely. The most important thing is that you must NOT call endBackgroundTask immediately after playSoundFile. Delay it for about 5-10 seconds.

Here is how I managed doing it for iOS6 and iOS7.

1, Add audio UIBackgroundModes in plist.

2, Set audio session category:

3, Create an AVQueuePlayer in the main thread and enqueue an audio asset before the App enter background.

*For continues playing in background (like playing a playlist)*

4, Make sure the audio queue in AVQueuePlayer never become empty, otherwise your App will be suspended when it finishes playing the last asset.

5, If 5 seconds is not enough for you to get the next asset you can employ background task to delay the suspend. When the asset is ready you should call endBackgroundTask after 10 seconds.

like image 156
Jay Zhao Avatar answered Oct 17 '22 00:10

Jay Zhao