AVAudioPlayer Loses "Playing" Status - ios

I have an AVAudioPlayer that needs to continue in the background.
Audio is set as the background mode in the plist & this runs on launch:
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[[RootController shared].view becomeFirstResponder];
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setDelegate: self];
[session setActive:YES error:nil];
[session setCategory: AVAudioSessionCategoryPlayback error:nil];
- (BOOL)canBecomeFirstResponder { return YES; }
The Problem
Occasionally the AVAudioPlayer gets in this strange state where:
It's playing, but the play icon in the status bar disappears
If I pause then play, the icon shows up for maybe a second, then disappears
Here's the kicker - if I call setCurrentTime while playing, the play icon shows & stays
I've sunk about 20 hours into this & would love any ideas.

Description of the Bug
If you are playing an AVAudioPlayer then you create an AVPlayer, the playing icon will disappear. Apparently AVPlayer immediately takes precedence & since it is not playing yet the play icon disappears from the status bar.
This causes some serious issues:
If the app is in the background, iOS will shut down your AVAudioPlayer within 5 seconds (because it doesn't realize you're playing audio)
The iOS remote shows a play button even though audio is playing
The play icon is not showing in the status bar
The Workaround
First off, if you don't have to use AVPlayer, then don't. I use it because I need to play a remote MP3 without downloading it first. I used to use this AudioStreamer class but gave up because it pops up an alert when the stream becomes disconnected along with a few other bugs that I couldn't fix.
So if you're stuck with AVPlayer, there's only one way to re-connect playing status with your AVAudioPlayer. If you call setCurrentTime on the AVAudioPlayer then it will magically re-associate itself as the current player for the app. So you'll need to call it after any AVPlayer is initialized and anytime you resume playback on your AVAudioPlayer.
I decided to subclass AVAudioPlayer so I could register it in a global list (when it is initialized) and unregister it when it is deallocated. I also overrode the play method so that any calls to resume playback would also call setCurrentTime. Then I subclassed AVPlayer so that any time one is initialized, all active AVAudioPlayers call setCurrentTime on themselves. Last thing - you'll have to call setCurrentTime after a short, maybe 1 second, delay or else it will have no effect.
No kidding, this is the result of nearly 40 hours of troubleshooting.

Related

Keeping the video player silent after the recording is completed

How to keep the video player silent after the video recorder has been initialized and the silent switched is turned on?
In the application I am working on I have a video player (will call it player) and a video recorder (will call it recorder). If the phone is in silent mode (the switch on the side of the device is turned on to disable the sound) the player is kept silent when entering the application which is an expected and desired result. But after the recorder has begun running the session this behavior is lost and the video will play out loud although the silent mode is enabled.
I know specifically where this happens is when startRunning is called on an AVCaptureSession.
I also know the reason is somewhere in [AVAudioSession sharedInstance] category or mode property which seem to be changed right after the session begins running.
So what I do at the moment is call
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategorySoloAmbient error:&error];
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeDefault error:&error];
after the recording is completed to reset the session. The issue is this works in most but not all cases, sometimes reports error, sometimes works with a delay, sometimes doesn't work at all.
The possible solutions I am looking for are (any would do):
Somehow prevent the recorder to enable sound playing
Have a clear way of resetting the category and mode after the recording has completed (no errors or misbehaving)
Have a clean solution to find the state of the silence button (seems the old functions are deprecated and I found nothing but hacks)
Any other ideas welcome
An important information is that I have both the player and a recorder initialized all the time for a quick usage. Also I can control the player to be mute.
What I use for player:
AVFoundation
AVPlayer
AVPlayerLayer
The source is an AVAsset representing a remote URL
The implementation is pretty standard.
What I use for the recorder:
AVFoundation
AVCaptureDeviceInput
AVCaptureMovieFileOutput
AVCaptureVideoPreviewLayer
Again all pretty standard.

MPMusicPlayerController Play music while app is in background

Hope this question doesn't get down voted - I haven't seen anything in the documentation.
I want to play music from the ipod library while the app is in the back ground.
I am creating the music player using the default mechanism.
-(void)viewWillAppear:(BOOL)animated
{
MPMusicPlayerController *myPlayer =
[MPMusicPlayerController applicationMusicPlayer];
// assign a playback queue containing all media items on the device
[myPlayer setQueueWithQuery: [MPMediaQuery songsQuery]];
// start playing from the beginning of the queue
[myPlayer play];
}
As soon as the app enters the background the music stops playing. I would like it to continue playing though.
I do not want to use the iPodMusicPlayer option
How can I do this?
You should set background audio mode in the project settings (Project settings > Capabilities > Background modes > Audio and AirPlay) like it's shown on the screenshot below:
Also you should set Audio Session Category in the appDelegate (in the didFinishLaunching method). Example is shown below:
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&setCategoryErr];
[[AVAudioSession sharedInstance] setActive:YES error:&activationErr];
I have same issue. I searched even after added flag in plist. It will not work because :
The applicationMusicPlayer does not support background music. Use
MPMusicPlayerController's systemMusicPlayer instead. It shares the
state with the built-in System player and music will continue to play
when your app enters the background.
Other Solution is :
You can use AVAudioPlayer to play either streamed or local(bundle) audio. It even allows you to play multiple audio file at the same time having an AVAudioPlayer instance for each.
Note : iPodMusicPlayer is deprecated. so Instead of this use systemMusicPlayer.

How can I avoid interrupting audio that is already playing when my app launches?

I have an app that needs to use AVAudioSessionCategoryPlayback in order to play sound while the device is locked or my app is in the background. (It's an alarm feature.) So I also need to set the "audio" key in my UIBackgroundModes list.
I have put the following code in my application:didFinishLaunchingWithOptions: method:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:nil];
However, I don't want to interrupt any music that is already playing on the device. When my app is launched, it causes Spotify (for example) to stop playing music. I can switch to Spotify and press play, and when I switch back to my app, it continues playing.
But this is very annoying to the user - alarms are not the only thing my app does, and there are many reasons for them to launch it that don't involve setting alarms. I don't think the user would ever want my app to stop any other audio playing on the device.
Ideally I'd never interrupt already-playing audio. I could also live with stopping audio when an alarm is set (at which point I have to activate my audio session to ensure that I'm able to play it) but it seems like there is no way for me to avoid the audio stop at app launch.
Is there some way to start my app with the audio session disabled, or to start it with this option already applied?
It turns out that I was calling .prepareForPlayback() on an AVAudioPlayer in an init() method which was getting called long before my didFinishLaunchingWithOptions: fired.

AVAudioPlayer, using AmbientSound session, is not playing when app is in background

If this is in the apple doc then I've not been able to find it - hoping someone can help:
My app plays occasional short audio clips - I want the audio to mix in with audio playing from other apps in the background like the iPod app - but I also want it to carry on playing these audio clips when the app is running in background.
I have set "App plays audio" in the Required Background Modes settings in info.plist (the app is also using location services too so that is also set in there)
My app sets up an audio session on applicationDidFinishLaunching:
AudioSessionInitialize (NULL,NULL,NULL,NULL);
UInt32 sessionCategory = kAudioSessionCategory_AmbientSound;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory,sizeof (sessionCategory),&sessionCategory);
AudioSessionSetActive(true);
In my viewWillAppear: method in the view that is active I have:
[super viewWillAppear:animated];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
and the corresponding event handler and endReceivingRemoteControlEvents code in viewWillDisappear: as discussed in iOS 4: Remote controls for background audio
Finally I have an AVAudioPlayer, set up in the normal way, that plays a sound on certain events
bool successful = [avAudioPlayer play];
if(!successful)
NSLog(#"did not play");
When the app is in foreground the app works fine and plays the audio - but when the app goes into background and the app attempts to play a sound the return value from the [avAudioPlayer play] is NO and the sound does not play - when switched back to foreground the audio starts working again.
If when I set up the session I instead use
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
Then the audio plays in foreground and background perfectly. But MediaPlayback is not the really the right mode for this app since I am only occasionally playing audio clips - AmbientSound is really the mode I should be using.
What am I missing? Is it just not possible to use kAudioSessionCategory_AmbientSound to play Audio in the background? If so I've not found anything in the documentation about that.
Had to submit a tech support request for this in the end.
According to Apple background playback is not supported by the AmbientSound category - you have to use MediaPlayback.
They claim this is in the documentation - I've looked again and I could not find it.
Ok so getting background sounds to mix into MediaPlayback is easy enough using kAudioSessionProperty_OverrideCategoryMixWithOthers - but I am now going to have to jump through some other hoops to replicate the other AmbientSound functionality (obeying the mute switch and not playing when locked). I really don't understand why AmbientSound is not supported playing in background - but there we go.

Continue playing audio from html5 player when in iOS background mode

We built our music-oriented app in html5 and javascript with Sencha touch. For distribution we wrapped it in xcode with UIwebView. Everything runs fine except one thing that does not work: audio playing in multitask mode.
I know the general idea: add the UIBackgroundModes in info.plist.
Done. Now we can play the audio even in background mode.
Until we reach the end of the song. To start the next song we have to bring the app to foreground again or we can hit the play or 'next song' button on the iPhone audio controller.
After some research I found a promising workaround at: " Entering background on iOS4 to play audio " where the workaround is to edit AVAudioSessionCategoryPlayback and work with the UIBackgroundTaskIdentifier.
The problem for me is (just like in any other fix I found so far) that those solutions always assume that the audio is played either with AVaudioPlayer or MPMusicPlayerController. But in my case I user neither, our audio is played by our html5 player wrapped in UIwebView.
Anyone has any advice on how to continue playing the audio in iOS multitask mode when the audio player is a html5/javascript player?
My app plays audio via an <audio> tag in html hosted in UIWebView, and it supports background playing.
As you suggest, you need to have the 'App plays audio' background mode defined in your plist.
Try adding this to your applicationDidFinishLaunching: handler:
NSError *setCategoryError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &setCategoryError];
Thanks, guys, but I ended up simply keeping the app busy in a loop when in background mode. This was enough to bridge the time when connecting to the next song on the playlist.
The code I used is similar to this one:
iPhone - Backgrounding to poll for events
You could try this, adding a function on the onend event.

Resources