AVAudioPlayer does not play when app goes to background - ios

I am trying out the AVFoundationFramework.
Presently I am able to run the AVAudioPlayer in the foreground.
When the app goes to the background, the AVAudioPlayer does not continue to play.
The exact steps of implementation are:
Adding Background Tasks to the .plist
Setting up AVAudioPlayer
self.objAVAudioPlayer = [[AVAudioPlayer alloc]initWithData:mp3Data error:&error];
self.objAVAudioPlayer.delegate = self;
Setting up AVAudioSession
if([self.objAVAudioPlayer prepareToPlay])
{
AVAudioSession *objAVAudioSession = [AVAudioSession sharedInstance];
[objAVAudioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
[objAVAudioSession setActive:YES
error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self.objAVAudioPlayer play];
}
When the app is resumed, it starts from the exact same place where it resigned.
The base SDK is iOS 7.0.
Any idea as to what I am missing? Any help will be appreciated.

For playing audio in background mode you should set up AVAudioSession and add this code
UIBackgroundTaskIdentifier backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:backgroundTask]; //Tell the system that we are done with the tasks
backgroundTask = UIBackgroundTaskInvalid; //Set the task to be invalid
}];
into your AppDelegate.m class in method
- (void)applicationDidEnterBackground:(UIApplication *)application {
//Some code
}

Related

Play audio while app in background not working

I've searched all over but cannot get this to work consistently. I want to play audio when a remote push notification arrives while the app is in the background or lock screen and the ringer is off.
Steps I've followed:
1) Set Required Background Modes to "App plays audio" into info.plist.
2) In application:didFinishLaunchingWithOptions:
a) set Audio Session category to Playback
b) make Audio Session active.
c) make app receive remote control events and become first responder
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// https://developer.apple.com/library/ios/qa/qa1668/_index.html
// For playback to continue when the screen locks, or when the Ring/Silent switch is set to silent, use the AVAudioSessionCategoryPlayback
BOOL result = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&sessionError]; // TODO AVAudioSessionCategorySoloAmbient
if (!result && sessionError) {
NSLog(#"AppDelegate error setting session category. error %#", sessionError);
}
else {
NSLog(#"AppDelegate setting session category is successful");
}
result = [audioSession setActive:YES error:&sessionError];
if (!result && sessionError) {
NSLog(#"AppDelegate error activating audio session. error %#", sessionError);
}
else {
NSLog(#"AppDelegate setting session active is successful");
}
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
3) In applicationDidEnterBackground:
a) begin background task
b) receive remote control events
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"AppDelegate applicationDidEnterBackground: called");
NSLog(#"AppDelegate applicationDidEnterBackground: is calling beginBackgroundTaskWithExpirationHandler:");
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
NSLog(#"AppDelegate applicationDidEnterBackground: is calling beginReceivingRemoteControlEvents");
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
}
4) When the remote notification comes in, play an audio file
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
_currentItem = [AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"Audio" withExtension:#"wav"]];
_audioPlayer = [AVPlayer playerWithPlayerItem:_currentItem];
[_currentItem addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
[_audioPlayer addObserver:self forKeyPath:#"status" options:NSKeyValueObservingOptionNew context:nil];
NSLog(#"playAudio: calling [_audioPlayer play] on audioPlayer %#", _audioPlayer);
[_audioPlayer play];
}
It works sometimes, but not always. Any ideas how to make this work consistently?
I think that you can't start playing background audio while already in the background, even starting in applicationDidEnterBackground is too late. A work around you could do is play silent audio or pause audio maybe. You could use the queue player to achieve this by looping the silent audio and seeking to the beginning when it ends, then enqueue the audio you'd like to play after it.

The UILocalNotification sound does not stop playing

I have set a custom .aif 30 second file as the local notification sound name. And below is my code for scheduling the local notification.
//Function to schedule local notification
-(void)schedulelocalnotification:(NSDate *)particularfiredate ringtone: (NSString *)particularringtone name:(NSString *)alarmname info:(NSDictionary *)dicttext
{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.fireDate = particularfiredate;
notification.soundName = [arrayAIFFFiles objectAtIndex:[arraysoundfilesnames indexOfObject:particularringtone]];
notification.alertBody = alarmname;
notification.userInfo = dicttext;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
But when the device is locked, and the user slides on the notification to enter the app, the sound keeps on playing even when the user enters the app. It continues to play even when the user quits/uninstalls the app.
Please suggest what could be the possible reasons.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[[NSUserDefaults standardUserDefaults] setValue:#"0" forKey:#"demo"];
NSLog(#"%i",[[[UIApplication sharedApplication] scheduledLocalNotifications] count]);
NSString *viewcontrollerstring = [notification.userInfo objectForKey:#"smiletosnooze"];
NSLog(#"++++++------%#",viewcontrollerstring);
}
PS: I checked - UILocalNotification stop sound after notification is dismissed and this - Stop UILocalNotification Sound on slide to view but it was of no help. :(
This appears to be an open bug with iOS 7 as filed here. It also seems that when a device is passcode-locked, this issue does not appear. A pretty ugly hack that worked for me is setting a value for the application badge number and removing it immediately when the app comes into foreground. A sample code would be:
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 1];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0];
}
EDIT:
Apparently, the above mentioned hack is not actually working on iOS 7.1+. The only work-around I found is the following, but I'm very hesitant in calling it an actual answer:
- (void)applicationWillEnterForeground:(UIApplication *)application
{
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
[session setActive:YES error:nil];
MPMusicPlayerController *musicPlayer = [MPMusicPlayerController applicationMusicPlayer];
[musicPlayer setVolume:0.0f];
}
But there are a number of serious flaws with the code above:
setVolume method is deprecated since iOS 7 (although apparently many apps are still using it)
You have at some point of your app to re-set the volume to a proper (non-zero level)
Setting the volume property will most definitely have side-effects in other apps that might be playing sounds or music at the same time
UPDATE September 18, 2014
This issue seems to be resolved on iOS 8.

AVPlayer play audio from iOS 7 background notification

I have audio, fetch and remote-notification set in UIBackgroundModes and I successfully receive remote notifications with my app in the background (not active) via:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
I have the following in my: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions:
self.audioPlayer = [[AVPlayer alloc] init];
NSError *sessionError = nil;
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive:YES error:&activationError];
if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&sessionError]) {
NSLog(#"[AppDelegate] Failed to setup audio session: %#", sessionError);
}
And in - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler I have the following:
NSLog(#"Playing url: %#", filePath);
AVPlayerItem * currentItem = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:filePath]];
[self.audioPlayer replaceCurrentItemWithPlayerItem:currentItem];
[self.audioPlayer play];
I see this code execute via NSLog but no sound is produced. Actually, if the app receives a notification within a few seconds of going to the background, audio does play, or ie. the first time it gets a notification audio plays, but never after that.
Can an app in iOS 7 initiate audio output asynchronously like this, from the background, ie. after it has been asleep and not produced any audio for some time?
You cannot initiate audio in the background. The only thing the audio background mode allows you to do is to continue producing sound as the app goes from the foreground to the background.
And this is a perfectly reasonable rule. It would be terrible if any app that happened to be running in the background could suddenly start producing sound out of the device whenever it likes, to the mystification and annoyance of the user!
However, if your app is capable of receiving remote events, and if it has produced sound so that it is the remote event target, then, with audio background mode, it can go on being the remote event target and thus can produce sound in the background, as long as no other app becomes the remote event target in the meantime.
The most reliable way to produce a sound while in the background is by attaching the sound to a local notification.

Starting AVQueuePlayer from background

Can't make AVQueuePlayer start playing sound queue when it's starting after the app went into the background.
The basic question is: how to start sound from newly created AVQueuePlayer instance from background?
It's for navi-like app that need to play couple of combined sounds with appropriate directions when the time comes. And most of the time the app works in background...
The details are below...
It plays just fine when I start it from active application, and finishes playing sound even after app went to background.
What I did so far:
In AppDelegate inside didFinishLaunchingWithOptions I added:
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&sessionError];
// Change the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
After the app started I clicked the home button so the app went into the background.
When the time came this code executes (the app is still in background, but note, that I have enabled Audio and AirPlay background mode):
-(void)playTrainingFinishedSound
{
NSMutableArray *queue = [NSMutableArray array];
[queue addObject:[AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:#"alertkoniectreningu" withExtension:#"m4a"]]];
[self initializeAudioPlayerWithQueue:queue];
[self.appDelegate.audioPlayer play];
}
-(void)initializeAudioPlayerWithQueue:(NSArray *)queue
{
self.appDelegate.audioPlayer = [[AVQueuePlayer alloc] initWithItems:queue];
self.appDelegate.audioPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
}
Unfortunately this code doesn't make any sound, opposite to the situation when the app was in foreground.
Oh God, there was just one line missing in the AppDelegate didFinishLaunchingWithOptions [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
so it looks now like:
audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];
if (audioSession) [audioSession setActive:YES error:nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
// Change the default output audio route
UInt32 doChangeDefaultRoute = 1;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker,
sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
The reason the AVPlayer is failing is because you can't create/initialize an Audio Unit while an application is in the background, which is what the AVPlayer is attempting to do under the hood. The exception to this rule is of course audio-playing applications which can be started/resumed via the "Play" buttons built into the OS, while they are in the background. Thus, applications which subscribe to remote control events have the capability to start Audio Units in the background, which is why subscribing to these events appears to solve the problem.
It's unclear whether Apple is OK with using the API feature in this way.

background mode in ios by audio being killed by other apps

Recently,I try to make an app needs working in background and I use audio to protect the app from being killed by ios.Neither voip nor bluetooth,and not a Luckily,the demo works well until a new app execute which also run in background and also run by something audio.Then the demo was killed after 10 mins later,can anyone help me with the trouble?There must be other Thanks!!later i found an new app which do well in background mode.And here is the url :https://itunes.apple.com/us/app/pacer-pedometer-plus-weight/id600446812?mt=8 ,Sorry ,i'm not good at in English..
- (void)applicationDidEnterBackground:(UIApplication *)application{
if(counter==0){
[player play];
// Registers this class as the delegate of the audio session.
[[AVAudioSession sharedInstance] setDelegate: self];
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 0;
//The C Style function call
AudioSessionSetProperty (
kAudioSessionProperty_OverrideCategoryMixWithOthers,
sizeof (doSetProperty),
&doSetProperty
);
// Activates the audio session.
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
[player setDelegate:self];
//play audio
[player play];
[self backgroundHandler];
}
}

Resources