I have an app that plays streaming audio from a SHOUTcast server. Everything works fine when the app is on the foreground and auto-lock is disabled. The app is however also able to play audio in the background, this feature has always been working fine on iOS 6 and iOS 7. But now my users are reporting that background audio stops after about 10 minutes after they upgraded to iOS 8.
I'm able to reproduce the problem myself by simply running the app on iOS 8. Since the app itself is pretty complicated, I've made a simple demo to show the problem. I'm using Xcode 6 and Base SDK is set to iOS 8. I've added audio to UIBackgroundModes in my Info.plist. Does anybody know what's wrong with the code below?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSURL *streamingURL = [NSURL URLWithString:@"http://www.radiofmgold.be/stream.php?ext=pls"];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:streamingURL];
[self setPlayerItem:playerItem];
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
[player setAllowsExternalPlayback:NO];
[self setPlayer:player];
[player play];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
return YES;
}
Sound settings: Sometimes, your iPhone's volume levels get turned down accidentally due to bumps inside your bag or fidgeting. Other software settings like the 'Do not disturb mode', 'Silent mode', or even sound enhancements can also cause this issue.
I experienced the same problem under iOS 8.0.2. My remote audio source plays in mp3. It seems an internal error is causing the AVAudioSession to restart. You can handle that in different ways:
You can observe the state of the AVPlayerItem.
void* YourAudioControllerItemStatusContext = "YourAudioControllerItemStatusContext";
...
@implementation YourAudioController
...
- (void)playStream {
...
AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:streamUrl];
[item addObserver:self forKeyPath:@"status" options:0 context:MJAudioControllerItemStatusContext];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == YourAudioControllerItemStatusContext) {
AVPlayerItem *item = object;
if (item.status == AVPlayerItemStatusFailed) {
[self recoverFromError];
}
}
This way seems not to be reliable since there can be an immense time delay until the state of the AVPlayerItem changes - if it changes at all.
I debugged through observeValueForKeyPath and found out that the state of the AVPlayerItem changes to AVErrorMediaServicesWereReset and has an error code -11819 set.
Since I experienced the AVErrorMediaServicesWereReset error I researched what causes this error - it's the AVAudioSession going crazy. Referring to the Technical Q&A from Apple you should register for the AVAudioSessionMediaServicesWereResetNotifications. Put that somewhere in your code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionWasReset:) name:AVAudioSessionMediaServicesWereResetNotification object:nil];
Then add this method:
- (void)audioSessionWasReset:(NSNotification *)notification {
[self recoverFromError];
}
In the recoverFromError method try one of the following:
At the moment that are the only ways I know how to handle it. The question is why the AudioSession is getting restarted.
UPDATE The release of iOS 8.1 seams to have solved the issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With