Run app in background only if audio os playing - iphone

Is it possible to run an app in background only if audio is playing?
I have made an app and I want that when the app goes in background, it will check if audio is playing or not. If audio is playing, then the app will run in background else the app will close.
TO DO this I have set in info.plist
Application does not run in background YES
Then audio is not playing in background.
Now to run audio I set
if(AUDIO_IS_PLAYING==NO){
exit(0);
}
But I think apple will not permit this.
Any other ways if you know to run the app in background only if audio is playing else it will be closed.

Use notifications:
Following notification is posted when the application enters the background.
UIApplicationDidEnterBackgroundNotification
Following notification is posted when the application becomes active.
UIApplicationDidBecomeActiveNotification
Add notification observer in function
//Adding observer for notification
-(void)viewDidLoad
{
// Your other code goes here
// Adding observer for notification when application entered the background
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationDidEnterBackgroundNotification:) name:UIApplicationDidEnterBackgroundNotification object:nil];
}
// This method will be called when application entered in background
-(void)applicationDidEnterBackgroundNotification:(id)sender
{
// Do whatever you want when application in background
}

Related

Implementing and Troubleshooting Background Audio in iOS

There are a lot of questions relating to background music playback in iOS on StackOverflow. None fully explore all edge cases, the aim of
this question is to be the final word in background audio question on
iOS
Definitions & Assumptions
All the code, questions and examples refer to ios5.
"background" — The state an app is put into when the user presses the home button or the power button (so the devices displays the lock screen). The app can also be put into background using the multitasking switcher or the multitasking gestures on iPad.
"audio" — Audio played back using AudioQueue (including AVAudioPlayer)
Prerequisites
As I understand it there are 2 requirements to get an app to play audio in the background.
Set UIBackgroundModes to audio in the Info.plist
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
Requirements
My use-case is playing relatively long audio in the background (music). There are potentially hundreds of tracks and the app will play them sequentially. It can be considered that the audio will play indefinitely.
The app will handle interruptions by pausing the playback.
Questions
I've had mixed success with:
[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:...];
Allowing audio to play in the background. But I'm confused as to if its required and how it differs to:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
Edge Cases
Interruptions. If you register to be notified of audio interruptions (phone calls etc), by becoming the delegate of AVAudioPlayer. For example, if you then pause or stop your audio when an interruption starts and resume when it ends is your app suspended if the interruption exceeds 10 minutes (max time allowed for background tasks to complete)?
The Simulator will stop the audio if Lock or Home are invoked, while using:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
However this works on a device. Is this a known issue?
I have some experience with GPS background mode, and background audio. This is not exactly the same as your situation (you want to play a long audio file, and I play short messages) but here's what I can tell you:
beginBackgroundTaskWithExpirationHandler This selector has one purpose when being invoked when in background: avoid the application to return to the suspended state in which no code can be invoked anymore (you're "frozen"). So as long as you invoked beginBackgroundTaskWithExpirationHandler and before you terminated your long running task with beginBackgroundTaskWithExpirationHandler, you use the CPU, and consume battery.
I really doubt that playing a file in the background should use the battery of the iPhone as if it was running an app so I doubt that beginBackgroundTaskWithExpirationHandler is really involved in your flow.
Simulator: don't rely on the simulator: it does not fully implement background modes. Actually, when you click on the home button, your app goes in background, but at this stage, you may still be able to execute code in your app. After a while, then, your app will be suspended (=frozen), and your code execution will be suspended in order to save the battery.
This suspended state will never occur on the simulator.
Interruptions. It's not up to you to pause/resume the playback when a phone call comes in. the platform is in charge of this, and you can just react to this with your AVAudioSessionDelegate . However, you can influence the way your session is going to interact with other audio sounds by setting property on your audio session (see kAudioSessionProperty_OverrideCategoryMixWithOthers for instance).
So the flow is more: your describe the way your audio session should interact with the rest of the system, the system will mix the sounds accordingly to that, and if your session gets interrupted, you'll be notified with the AVAudioSessionDelegate.
Hope this helps.
I have used below code to Device Control -
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
Used to get register for listening the remote control.
Once done remove it -
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];
make the App canBecomeFirstResponder-
- (BOOL)canBecomeFirstResponder {
return YES;
}
Used delegate method to handle iPhone control, like play and pause while doble tap on the home button
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
//if it is a remote control event handle it correctly
if (event.type == UIEventTypeRemoteControl) {
if (event.subtype == UIEventSubtypeRemoteControlPlay) {
[audioPlayer play];
NSLog(#"play");
} else if (event.subtype == UIEventSubtypeRemoteControlPause) {
[audioPlayer stop];
NSLog(#"pause");
} else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause) {
NSLog(#"toggle");
}
}
}

iPhone iOS do apps running in backgrounds generate and process notifications?

My app is downloading JSON objects in when the app enters background mode. The app converts them to core data entities. The issue that I'm running in is that I need to merge the managed object contexts for these core data entities with the main managed object context.
The way to merge these changes is through notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(contextHasChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];
- (void)contextHasChanged:(NSNotification*)notification
{
NSLog(#"received notification with object: %#",[[notification object] description]);
if ([notification object] == [AppUser managedObjectContext]) return;
if (![NSThread isMainThread]) {
[self performSelectorOnMainThread:#selector(contextHasChanged:) withObject:notification waitUntilDone:YES];
return;
}
[[AppUser managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
}
For some reason my code does not receive these notifications while running in background. Does the app continue to generate notifications while it is running in background mode? Or is it something with the location of where/when I register for such notifications that's wrong?
Thank you for the clarification!
app continues to send notifications either in main or background. you need to take care of
the observer should not be released during add observer and posting notification. i thnk there may some mistak in implementaion read this
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/CoreDataFramework/Classes/NSManagedObjectContext_Class/NSManagedObjectContext.html
Once you press the Home button, your app goes into suspended mode. It won't process the above notifications until you "wake" it up by tapping on its icon.
To ensure that your app continues to do its task, you need to request background task completion. The OS will then give you up to 600 seconds (10 minutes) to complete whatever task your app is doing before suspending it.
You can read more about it here: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW3
Specifically, look for "Background Execution and Multitasking" in the above link.
There are limitations to what type of notifications you can receive while in the background. Also the sending of notifications is something you schedule before entering the background.
If you need to continue doing work when the app enters thebackground you should call beginBackgroundTaskWithExpirationHandler as well.
Main documentation is here:
http://developer.apple.com/library/ios/ipad/#DOCUMENTATION/iPhone/Conceptual/iPhoneOSProgrammingGuide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html
Scroll down to the section in Background Execution and Multitasking

Responding to MPMoviePlayerController notifications during background media playback

I have an app that streams video from the net and plays it using an MPMoviePlayerController object for playback on the device or via AirPlay.
The app supports background operation and has the 'audio' option listed within the required UIBackgroundModes key in its plist file.
When playing over AirPlay, the app can successfully be pushed to the background and the video continues to play properly. So far, so good.
According to the Apple documentation:
Including the audio key tells the system frameworks that they should
continue playing and make the necessary callbacks to the app at
appropriate intervals. If the app does not include this key, any audio
being played by the app stops when the app moves to the background.
However, these callbacks are not being made.
The app uses two types of callback: those associated with notifications MPMoviePlayerController and AVPlayer send during playback together with timer based callbacks that monitor the playback position and performance stats for monitoring purposes.
Looking at Apple's notes, I would certainly expect to receive the first type of callback so that the app can respond to MPMoviePlayerPlaybackStateDidChangeNotification, MPMoviePlayerPlaybackDidFinishNotification and MPMoviePlayerLoadStateDidChangeNotification, but this doesn't happen.
Does anyone know if it is possible to receive these during background AirPlay playback and, if so, how was this achieved?
**Please note: the app works correctly when running in the foreground and receives the notifications fine. It is only when pushed to the background and playing over AirPlay that the notifications are not received.
Likewise, the video plays over AirPlay in the background properly. It is only the notifications which are not received**
I had this issue and have fixed it though it was a few months back. I could send you my entire class for movie playback if this doesn't work. Note it is using the navigation controller model.
NOTE: This is tested on iPad 2 not on the iPhone.
I show my VC like this:
- (IBAction)playMovie:(id)sender {
MovieVC* movController = [[MovieVC alloc] initWithID:2];
movController.view.backgroundColor = [UIColor blackColor];
AppDelegate *appDel = [[UIApplication sharedApplication] delegate];
[appDel.navigationController pushViewController:movController animated:NO];
[movController release];
}
Then in my MovieVC view controller class i set up the video playback like this:
- (void)initMoviePlayer {
mMoviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:[self getMovieURL]];
mMoviePlayer.allowsAirPlay = YES;
mMoviePlayer.view.frame = [self.view bounds];
mMoviePlayer.view.backgroundColor = [UIColor clearColor];
mMoviePlayer.shouldAutoplay = YES;
mMoviePlayer.fullscreen = YES;
mMoviePlayer.scalingMode = MPMovieScalingModeAspectFit;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePreloadDidFinish:)
name:MPMoviePlayerLoadStateDidChangeNotification
object:mMoviePlayer];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(moviePlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:nil];
}
This fixed it for me, if it doesn't fix comment and ill edit with the entire class file.
In one of my projects I did what you did, and it worked for me.
Two differences in my project :
I am not streaming via airPlay (device playback only),
I am just playing audio files.
My step-by-step :
Add the audio option to UIBackgroundModes in the plist file,
Register to the NotificationCenter for MPMoviePlayerPlaybackDidFinishNotification and MPMoviePlayerPlaybackStateDidChangeNotification with the following code :
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playbackStateChanged:)
name:MPMoviePlayerPlaybackStateDidChangeNotification
object:moviePlayer];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playbackEnded:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:moviePlayer];
It works like a charm.
For completeness, I ought to add that at present I believe there is no solution to this problem.
I've discussed it directly with Apple via tech support and there was no practical work-around available.
This functionality was needed to allow the app to record stats about the playback of the stream at regular intervals. While this is fine when the video is played on the device's screen and over AirPlay while the app is in the foreground, it isn't possible to do with the app in the background.
The solution I've gone with instead is to disable the idle timer during all types of playback and to re-enable afterwards using:
[UIApplication sharedApplication].idleTimerDisabled = YES;
and
[UIApplication sharedApplication].idleTimerDisabled = NO;
While this isn't a solution to the original question, it's a workaround for avoiding the issue in the first place.

iPhone App backgrounding with MPMusicPlayer

I'm working on an iPhone iOS4 application that incorporates playing music from the user's iPod library. I also want to keep track of what songs have been played and be able to change the song randomly, even while in the background. So I set the music player using:
[self setMusicPlayer: [MPMusicPlayerController iPodMusicPlayer]];
Now, I want this application to continue to run and play music in the background, so I have set:
Required background modes: App plays audio
The problem I'm having is that my application loses control when it is moved into the background (when applicationDidEnterBackground is called, ie. on app switches). Since I'm using the iPodMusicPlayer the music continues to play but my app does not have control and therefore can't track or change the song.
Now, the Apple documentation states that your application should continue to execute in the background using this required background modes tag, but mine does not. Is it because I'm using MPMusicPlayer? Is there any way to get around it? Any ideas?
PS. I'm also trying to get the remote locked and multitasking iPod controllers to work with my application. I'm using the code below, but remoteControlReceivedWithEvent never gets called! Does it work with MPMusicPlayer? I've only seen it with AVAudioPlayer.
- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
NSLog(#"remoteControlReceivedWithEvent");
switch (event.subtype) {
case UIEventSubtypeRemoteControlTogglePlayPause:
NSLog(#"Play Pause");
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(#"Next");
break;
default:
break;
}
}
- (BOOL)canBecomeFirstResponder {
NSLog(#"canBecomeFirstResponder");
return YES;
}
- (void) viewWillAppear:(BOOL)animated{
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
}
You are correct. Your iPhone app will not run in the background while using MPMusicPlayerController. This will also prevent you from receiving remote control events.
If you want to play audio from the iPod library and have your app continue running in the background, you must use the lower-level AVPlayer class.

How to know when the MPMoviePlayerController has been paused in iPhone?

I want to add an overlay view for my video when the video is paused by the user. Is there any way to get the pause notification from MPMoviePlayerController?
According to Apple Doc, there should be ways to do this but I can't find which notification should I use for this purpose.
Quote:
In addition to being notified when
playback finishes, interested clients
can be notified in the following
situations:
-When the movie player begins playing, is paused, or begins seeking forward
...
For more information, see the Notifications section in this reference.
I assume you know about delegates and protocols as a means of receiving callbacks?
There's another global mechanism called notifications too.
You can do this via
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playbackStateChanged)
name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
Then, within playbackStateChanged, you can fetch the playbackState
- (void) playbackStateChanged {
_player.playbackState; // reading the playback
}
The step of reading playbackstate directly from the player is specified in the docs
To get the current playback state, get the value of the playbackState property of the movie player object.