34

I've done my homework... been reading here, the docs, googling, stackoverflowing... but still no luck in making my sound stick when the user makes the app go into the background.

What I have done so far: Added the UIBackgroundModes, audio to the plist-file.

First this code:

radioAudio = [[AVAudioSession alloc] init];
[radioAudio setCategory:AVAudioSessionCategoryPlayback error:nil];
[radioAudio setActive:YES error:nil];

Then this:

NSString *radioURL = @"http://xxx.xxx.xxx/radio.m3u";
radioPlayer = [[AVPlayer playerWithURL:[NSURL URLWithString:radioURL]] retain];

But as soon as the user hits the home-button, my sound dies.

I also found this, but not added yet cuase some stuff I've read says it is not needed;

newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
     if (newTaskId != UIBackgroundTaskInvalid && bgTaskId != UIBackgroundTaskInvalid)
        [[UIApplication sharedApplication] endBackgroundTask: bgTaskId];
bgTaskId = newTaskId;

Right now I have no idea where I should go to make my AVPlayer let the radio sing while the user does other stuff on the phone. I am testing this on an iPhone 4 with 4.2. Building it for 4.0.

Anyone have any suggestions what I should do?

hypercrypt
  • 15,389
  • 6
  • 48
  • 59
swe_mattias
  • 621
  • 1
  • 8
  • 15
  • See My answer in this Que, you will find the your solution [Click Here to View the Answer][1] [1]: http://stackoverflow.com/questions/15470452/is-it-possible-to-play-video-using-avplayer-in-background – Vishal Khatri May 10 '13 at 13:47

7 Answers7

53

UPDATE IOS 11.2 with Swift 4:

Now if you are using AVPlayer to play music files you should also configure MPNowPlayingInfoCenter.default() to show now playing info on the lock screen.

Below code will show now playing controls on the screen but it won't be able to respond any commands.

If you also want to controls to work you should check apple's sample project here: https://developer.apple.com/library/content/samplecode/MPRemoteCommandSample/Introduction/Intro.html#//apple_ref/doc/uid/TP40017322

Apple sample code covers all but i find it confusing.

If you want to play sound and show controls on the lock screen these steps will do just fine.

IMPORTANT NOTE: If you are NOT using AVPlayer to play sound. If you are using some third party libraries to generate sound or playback a sound file you should read comments inside the code. Also if you are using ios simulator 11.2 you won't be able to see any controls on lock screen. You should use a device to see it work.


1- Select project -> capabilites -> set background modes ON -> tick Audio, AirPlay and Picture in Picture

ios 11 background audio setting

2- AppDelegate.swift file should look like this :

import UIKit

import AVFoundation

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
    {
        // Override point for customization after application launch.

        do
        {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            try AVAudioSession.sharedInstance().setActive(true)

         //!! IMPORTANT !!
         /*
         If you're using 3rd party libraries to play sound or generate sound you should
         set sample rate manually here.
         Otherwise you wont be able to hear any sound when you lock screen
         */
            //try AVAudioSession.sharedInstance().setPreferredSampleRate(4096)
        }
        catch
        {
            print(error)
        }
        // This will enable to show nowplaying controls on lock screen
        application.beginReceivingRemoteControlEvents()

        return true
    }
}

3- ViewController.swift should look like this:

import UIKit

import AVFoundation
import MediaPlayer

class ViewController: UIViewController
{

    var player : AVPlayer = AVPlayer()

    override func viewDidLoad()
    {
        super.viewDidLoad()


        let path = Bundle.main.path(forResource: "music", ofType: "mp3")
        let url = URL(fileURLWithPath: path!)

        // !! IMPORTANT !!
        /*
            If you are using 3rd party libraries to play sound 
            or generate sound you should always setNowPlayingInfo 
            before you create your player object.

            right:
            self.setNowPlayingInfo()
            let notAVPlayer = SomePlayer()

            wrong(You won't be able to see any controls on lock screen.):
            let notAVPlayer = SomePlayer()
            self.setNowPlayingInfo()
         */

        self.setNowPlayingInfo()
        self.player = AVPlayer(url: url)

    }


    func setNowPlayingInfo()
    {
        let nowPlayingInfoCenter = MPNowPlayingInfoCenter.default()
        var nowPlayingInfo = nowPlayingInfoCenter.nowPlayingInfo ?? [String: Any]()

        let title = "title"
        let album = "album"
        let artworkData = Data()
        let image = UIImage(data: artworkData) ?? UIImage()
        let artwork = MPMediaItemArtwork(boundsSize: image.size, requestHandler: {  (_) -> UIImage in
            return image
        })

        nowPlayingInfo[MPMediaItemPropertyTitle] = title
        nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = album
        nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork

        nowPlayingInfoCenter.nowPlayingInfo = nowPlayingInfo
    }

    @IBAction func startPlayingButtonPressed(_ sender: Any)
    {
        self.player.play()
    }

OLD ANSWER IOS 8.2:

Patrick's answer is totally right.

But i'm gonna write what i do for ios 8.2:

I add my app's info.plist required background modes like below:

enter image description here

And in my AppDelegate.h i add these imports:

#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

Then in my AppDelegate.m i wrote application didFinishLaunchingWithOptionsthis exactly like below:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

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

    return YES;
}

Now App keeps playing music even if screen is locked :)

EFE
  • 3,732
  • 4
  • 22
  • 30
  • 1
    solution worked but jyfi in Swift and ios 8.4 I did not need to import AudioToolbox – longbow Aug 28 '15 at 12:09
  • thanks. What are the keys and values to be added in `Info.plist` for iOS 11.2 ? –  Jun 28 '18 at 07:27
  • `try AVAudioSession.sharedInstance().setActive(true)` is not needed when using `AVPlayer`. It just makes other audio stop on launching your app, when you put it in your AppDelegate's `didFinishLaunching` function, which is not always wanted... – heyfrank Aug 26 '18 at 17:11
42

Had the same problem, but found a solution for this..

Look here: https://devforums.apple.com/message/395049#395049

The content of the above link:


Replace APPNAME with your own app name!

Im on iOS 4.2.1

EDIT: Working with iOS5 + 6 + 7 beta so far

Add UIBackgroundModes in the APPNAME-Info.plist, with the selection App plays audio

Then add the AudioToolBox framework to the folder frameworks.

In the APPNAMEAppDelegate.h add:

#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

so it look like this:

...
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
...

In the APPNAMEAppDelegate.m add the following:

// Set AudioSession
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:&sessionError];

/* Pick any one of them */
// 1. Overriding the output audio route
//UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
//AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);

// 2. Changing the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);

into the

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

but before the two lines:

[self.window addSubview:viewController.view];
[self.window makeKeyAndVisible];

Build your project and see if theres any error, If not, try debug on Device insted of the Simulator, it CAN bug on simulator.

Hope this helps others with same problem..

Patrick R
  • 1,949
  • 3
  • 32
  • 58
  • 2
    Thank you Patrick R! Made my background audio work perfect! :) – swe_mattias Mar 10 '11 at 10:27
  • I get an error " "_AudioSessionSetProperty", referenced from:" I have all the frameworks already added. Not sure why I'm getting it. – Roozbeh15 Jun 15 '12 at 05:08
  • You can try the Override insted of the change setting in the AudioSession. Just to see if that should even work. Altho it isnt a nice work-around. What iOS are you on atm ? – Patrick R Jun 15 '12 at 06:34
  • Hi, I have updated my code according to you, but unfortunately this works not for me in iOS 6.0+. Any suggestion ? – Sandeep Singh Dec 27 '12 at 07:58
  • If you are interested you can even play the sound of a video with `AVPlayer`, details [here](http://stackoverflow.com/a/15523272/616964) . – MacTeo Mar 22 '13 at 07:01
  • Only problem with this is AVPlayer continues to play when a phone call is in progress. How can we fix this? – TijuanaKez Oct 10 '13 at 04:23
  • When I set the delegate to self I get the following error: AppDelegate.m:18:50: Sending 'AppDelegate *const __strong' to parameter of incompatible type 'id' – 4GetFullOf Nov 02 '13 at 15:42
  • Also, please see http://stackoverflow.com/questions/15425968/play-video-in-background-using-avplayer – Anconia Jul 26 '15 at 17:27
  • UpVote for mentioning this but before the two lines: [self.window addSubview:viewController.view]; [self.window makeKeyAndVisible]; – Abuzar Amin Mar 02 '16 at 23:16
10

I have successfully made audio run in the background by adding the following to my applicationDidFinishLaunching

// Registers this class as the delegate of the audio session.
[[AVAudioSession sharedInstance] setDelegate: self];    
// Allow the app sound to continue to play when the screen is locked.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
atomoil
  • 364
  • 1
  • 7
  • That doesn't work for me, I must have messed the background stuff up somewhere else in the code. The only thing I can think of is that I have NSNotificationCenter running to check for different states of the app. I dont do anything with them atm, just reading the different states. – swe_mattias Jan 23 '11 at 08:58
  • 1
    What I hade to do, that I havn't found anything on the net that states this, is to register my audio to AVAudioSession as active, like this: [[AVAudioSession sharedInstance] setActive: YES error: NULL]; – swe_mattias Jan 24 '11 at 07:35
  • 1
    So it works with `[[AVAudioSession sharedInstance] setActive: YES error: NULL];`? Interesting to know, will remember that for the future :) – atomoil Jan 24 '11 at 10:24
  • Well today my AVPlayer isn't eorking any more and I have no clue why so far. Up until know it has been working flawlessly. Time to get digging. – swe_mattias Feb 10 '11 at 10:27
  • Did you manage to make this work ? Because I'm trying to do the same and it just won't work :-( – Patrice Cote Feb 28 '11 at 05:42
  • Yes it is working with:[[AVAudioSession sharedInstance] setDelegate:self]; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&sessionError];[[AVAudioSession sharedInstance] setActive: YES error: NULL]; – swe_mattias Mar 10 '11 at 09:55
  • 1
    I set UIBackgroundModes in the plist file manually and found it didn't work until I went to Project Settings -> Capabilities -> Background Modes and checked the first item in the list Modes: Audio, Airplay, and Picture in Picture. When you do that it gives you a message in "Steps:" asking you to add it to the plist file as well. – xdeleon Oct 26 '16 at 14:52
6

iOS 9 Swift

All you should need is add the following to your didFinishLaunchingWithOptions

do  {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
} catch {
    //TODO
}
Marc Nunes
  • 238
  • 3
  • 13
1

I've the same problem and I found solution in Apple Docs:https://developer.apple.com/library/ios/qa/qa1668/_index.html

Q: How do I ensure my media will continue playing when using AV Foundation while my application is in the background?

A: You must declare your app plays audible content while in the background, and assign an appropriate category to your audio session. See also Special Considerations for Video Media.

Andriy Savran
  • 535
  • 3
  • 19
1

You have a good example of a GPS background app that plays sounds even when in background :

http://www.informit.com/articles/article.aspx?p=1646438&seqNum=5

In this example, AudioToolbox is used.

I did a test myself and it works : create a simple project that monitors the GPS post (UIBackgroundModes location) , and every x received position, play a sound using

AudioServicesPlaySystemSound(soundId);

Then if you put audio as part of your UIBackgroundModes and the sounds will be played, even if the application isn't in foreground anymore.

I've made such a test and it works ok !

(I didn't manage to get it working with AVPlayer so I fallbacked to AudioToolbox)

yonel
  • 7,855
  • 2
  • 44
  • 51
0

Working for Swift 5

Here are the 4 things that I did for my music to play in the background from a video that the avPlayer was playing. I followed the Apple directions link from @AndriySavran's answer and this Apple link plus a few other things.

1- In AppDelegate's didFinishLaunching I put:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        do {
            try AVAudioSession.sharedInstance().setCategory( .playback, mode: .moviePlayback, options: [.mixWithOthers, .allowAirPlay])
            print("Playback OK")
            try AVAudioSession.sharedInstance().setActive(true)
            print("Session is Active")
        } catch {
            print(error)
        }

    // whatever other code that you use ...

    return true
}

2- Follow this answer from @LeoDabus. In your Signing & Capabilties > Background Modes (if background modes isn't there then select it from Capabilites then) > tick Audio, Airplay, and Picture in Picture

3- In the view controller that has your AVPlayer, add the .didEnterBackgroundNotification and .willEnterForegroundNotification notifications

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self,
                                           selector: #selector(appDidEnterBackground),
                                           name: UIApplication.didEnterBackgroundNotification, object: nil)
    
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(appWillEnterForeground),
                                           name: UIApplication.willEnterForegroundNotification, object: nil)

}

4- for the selector method add the code from the Apple link

@objc func appDidEnterBackground() {

    playerLayer?.player = nil
}

@objc func appWillEnterForeground() {
    
    playerLayer?.player = self.player
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256