Entering background on iOS4 to play audio

12,530

Solution 1

The one part missing from the documentation is you need to set your audio session.

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

Add that and you'll be good to go.

Solution 2

You also need to make calls to beginBackgroundTaskWithExpirationHandler: before you start a song (do it all the time whether in foreground or not) and endBackgroundTask when it ends.

This tells iOS that you want to be able to continue processing even if your app gets put in the background. It does not create a new task or thread, it's just a flag to iOS that you want to keep running. The audio will play to completion without this, but when it completes if you want to start another track you must be able to run in the background.

You can use it like this:

// ivar, initialized to UIBackgroundTaskInvalid in awakeFromNib
UIBackgroundTaskIdentifier bgTaskId; 

When starting an audio player we save the bg task id:

if ([audioPlayer play]) {
  bgTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
}

Now the audioPlayerDidFinishPlaying: callback will still occur because we have told iOS that we are doing a task that needs to continue even if we get put in the background. That way we get the callback and can start the next audio file.

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)success
{
    UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;

    if (self.haveMoreAudioToPlay) {
        newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
        [self playNextAudioFile];
    }

    if (bgTaskId != UIBackgroundTaskInvalid) {
        [[UIApplication sharedApplication] endBackgroundTask: bgTaskId];
    }

    bgTaskId = newTaskId;
}      
Share:
12,530
iwasrobbed
Author by

iwasrobbed

I work mostly with Swift/Objective-C and I open source a lot, including a video social network built with Ruby on Rails. I also dabble in Photoshop / Sketch. I am a Mechanical Engineer by training. Swift cheat sheet: https://github.com/iwasrobbed/Swift-CheatSheet ObjC cheat sheet: https://github.com/iwasrobbed/Objective-C-CheatSheet

Updated on June 24, 2022

Comments

  • iwasrobbed
    iwasrobbed almost 2 years

    The documentation is rather poorly written when talking about playing audio in the background. It gives the impression that all you have to do to continue playing the audio that you are currently playing is to just add a key/value pair to the info.plist file and wallah, it's magic.

    However, this is not the case. For instance, if I play an mp3 that is 2 minutes long, obviously the audio is long enough to be able to play after I hit the home button to send my app to the background. The thing is, even though I have that key/value pair in my info.plist file, it pauses the audio and then resumes playing once I switch back to the app.

    Apple states that all frameworks for audio support background and the sound should continue playing up until the point that it ends and then Apple will suspend your app.

    So, my question is: What are they doing that I am missing? Do I also have to use their new delegates somehow or call the audio on the applicationDidEnterBackground? To me, that wouldn't make sense since I am not saving state (they do for me with fast app switching) or really handling anything in the background other than audio which they say is supposed to be handled automagically.

  • Joshua Weinberg
    Joshua Weinberg almost 14 years
    Why would you start a background task just to play audio. This is not necessary at all.
  • iwasrobbed
    iwasrobbed almost 14 years
    Do you think this would keep it alive only for the time Apple allots when you request beginBackgroundTaskWithExpirationHandler or will it keep playing as long as you are using audioDidFinishPlaying to loop a sound or play a new sound?
  • Joshua Weinberg
    Joshua Weinberg almost 14 years
    At least someone realizes it! :)
  • iwasrobbed
    iwasrobbed almost 14 years
    +1 to progrmr for going above and beyond since i will indeed need that as well for what i am doing (not sure why someone downvoted you, but i appreciate your answer)
  • iwasrobbed
    iwasrobbed almost 14 years
    Josh, you were getting the checkmark, don't worry :) It worked like a charm. stackoverflow was screwing up on my slow network so i had to close the browser to correct it
  • Joshua Weinberg
    Joshua Weinberg almost 14 years
    Just playing around :) You should file a radar about the documentation bug if you feel it wasn't clear that it is dependent on the audio session.
  • iwasrobbed
    iwasrobbed almost 14 years
    Good point, I'll be sure to so we can save others from headaches. Thanks again
  • Joost Schuur
    Joost Schuur almost 14 years
    Do you really need to go through all the trouble with task completion to start background audio? This is contrary to the material in the WWDC 2010 video on the topic ('Session 109 - Adopting Multitasking on iPhone OS, Part 2') at around minute 24, they show their avTouch app being converted for background audio, and they key was to prevent further GPU usage when you're in the background because that's what will kill the app. This was done by registering for a UIApplicationDidEnterBackgroundNotification and preventing GPU usage while in the background.
  • progrmr
    progrmr almost 14 years
    @IWasRobbed: you need this if you want to switch audio tracks in the background (which wasn't exactly the original question) otherwise your app suspends when the audio finishes playing. See this discussion.
  • progrmr
    progrmr almost 14 years
    @Joshua: If you started the audio in the foreground and want to suspend when that audio file ends, then you don't need to do this. But you can't play the next audio track because you will never get the audioPlayerDidFinishPlaying callback if you're didn't tell the system that you are doing background processing by using beginBackgroundTaskWithExpirationHandler.
  • Alex1987
    Alex1987 almost 13 years
    @iWasRobbed: I found out that in addition you should use GCD in this case - dispatch_async(dispatch_get_global_queue(0, 0), ^{your audio playing code});
  • Vignesh
    Vignesh about 11 years
    @progrmr. +1. Your answer is a value add. I would have spent more time if you haven't posted your answer. Thanks. Great help.
  • Alex
    Alex over 10 years
    @progrmr, seems like you're not the only person who gave this as an answer, but I've implemented a music application on top of the Spotify framework and it works fine (when switching between songs in the background) even without initializing a background task. Either I am doing something wrong that I'm only going to see down the road, or maybe the way background audio works in iOS 6 has changed since 2010 (iOS 4)?
  • progrmr
    progrmr over 10 years
    @Andrei I haven't work on background audio for a few years so I can't really say what works now. A lot has changed since 2010.