8

I was trying to figure out what actually happens for weeks and I have no idea why I cannot continue playback after interruption, so probably you guys know an answer. AudioSessionSetActive(TRUE) always returns '!cat' which is kAudioSessionIncompatibleCategory while re-activation if my app plays in background and I am in different app. Although it works fine and continues playback if I caught interruption while being in my app.

Original code actually has all AudioSession and AudioQueue calls wrapped in macros which prints OSStatus if it means error, but I removed it for better readability. Also, [self pause] just toggles pause, so basically it calls AudioQueueStart(audioQueue, NULL) on upause but it doesn't work ofcourse if AudioSession fails.

Audio Session initialization code:

AudioSessionInitialize(NULL, NULL, _audioSessionInterruptionListener, self);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, _audioSessionPropertyListener, self);
AudioSessionSetActive(TRUE);

Interruption handler code:

- (void)handleInterruptionChangeToState:(AudioQueuePropertyID)inInterruptionState 
{
    if(inInterruptionState == kAudioSessionBeginInterruption)
    {

        NSLog(@"+Interruption"); 

        if(self.state == NX_STATE_PLAY) 
        {
            [self pause];
            AudioSessionSetActive(FALSE);

            isPausedByInterruption = YES;
        }
    }
    else if(inInterruptionState == kAudioSessionEndInterruption) 
    {
        if(isPausedByInterruption) 
        {
            AudioSessionSetActive(TRUE);
            [self pause];

            isPausedByInterruption = FALSE;
        }

        NSLog(@"-Interruption");
    }
}

This streamer source code can be found here https://bitbucket.org/and/amaudiostreamer/src/122de41fe6c0/AMAudioStreamer/AMAudioStreamer/Classes/NxAudioStreamer.m if it's gonna help somehow to resolve an issue..

pronebird
  • 12,068
  • 5
  • 54
  • 82

6 Answers6

3

If you are using the AudioQueue API you need to do some extra steps that depends on some factor. I've never done that, so I will leave the explanation to the expert :
there is a video on that topic in the Apple Developer website that cover that exact problem. WWDC 2010 session 412 Audio Development for iPhone OS part 1 around the 45th minutes you've got a pretty good explanation on that matter.

Vincent Bernier
  • 8,674
  • 4
  • 35
  • 40
  • I still can't get why AudioSessionSetActive(true); returns !cat, probably it doesn't even matter what it returns, all I have to do just continue playback. So basically I have to dispose and recreate AudioQueue while using hardware codec. This is very important, thanks for your reply. – pronebird Nov 04 '11 at 17:35
  • There is a lot in that video, if I remember correctly there is a section on AudioSession category, maybe there you will find something. – Vincent Bernier Nov 04 '11 at 17:45
2

I had a problem, when Alarm comes during app run, user just presses device power button making it go sleep. Then, after resume from sleep my AudioSessionSetActive fails with something like "this audiosession type can't be used". I tried to add set audiosession property before AudioSessionSetActive(true) in Interruptionlistener, but no luck. Finally I added

retry(~1000 times :) 

ftw)

AudioSessionSetActive(true), 

and it solved my problem.

Lenin
  • 570
  • 16
  • 36
NoAngel
  • 1,072
  • 2
  • 18
  • 27
  • I think I didn't completely manage to make it work those days, it was quite random behavior. Later on I switched to AVFoundation and AVSession that seems working. Honestly I think Apple had bugs in their SDK and I guess this code would work properly on iOS 6, but it's only assumption. – pronebird Dec 13 '12 at 13:39
0

Try activating AudioSession in else if condition as follows:

AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;

[session setCategory: AVAudioSessionCategoryPlayback error: &error];
if (error != nil)
    NSLog(@"Failed to set category on AVAudioSession");

// AudioSession and AVAudioSession calls can be used interchangeably
OSStatus result = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, RouteChangeListener, self);
if (result) NSLog(@"Could not add property listener! %d\n", result);

BOOL active = [session setActive: YES error: nil];
if (!active)
    NSLog(@"Failed to set category on AVAudioSession");

But I believe this may not work because in my case what happened is when i was in background it was not getting any session. But try to analyze the aurioTouch example of Apple and only go through AppDelegate file and Try to analyze (void)rioInterruptionListener method which explains the same problem.

Are you using Live Streaming of audio?? then i would recommend you to go through my question's answer, where a problem of queue start is solve by handling the error as given in my answer.

Hope either of this could be Helpful to you.

Community
  • 1
  • 1
DShah
  • 9,768
  • 11
  • 71
  • 127
  • As you expected, it doesn't work and gives me the same error in more fashionable way: "Failed to set category on AVAudioSession. The operation couldn’t be completed. (OSStatus error 560161140.)". I'll try to analyze rioInterruptionListener.. I think I just missed some small thing. – pronebird Nov 04 '11 at 07:34
  • then i believe firstly you must try the thing in my question's answer. But here there will be too tough to get the exact issue. You need to identify where `exactly` you want to modify the code. this was a challenge for me when i had done... Best of Luck to you... Hope you will be able to solve the issue... – DShah Nov 04 '11 at 07:43
0

the kAudioSessionEndInterruption may or may not hit your code, it's not a reliable way to control your play states, just don't turn the audio session off in your code, it will resume the session once it can gain control again, in your case, just comment out the AudioSessionSetActive(FALSE) will help you.

Allen
  • 6,505
  • 16
  • 19
0

If you look at Listing 7-16 An interruption listener callback function in the Audio Session Programming Guide cookbook section, the code sample (which seems to be compatible with your situation, using kAudioSessionCategory_MediaPlayback) doesn't actually perform the

AudioSessionSetActive(FALSE);

call in the case of kAudioSessionBeginInterruption, and the

AudioSessionSetActive(TRUE);

call in the case of kAudioSessionEndInterruption. I really don't think you should be doing this. This post seems to illustrate this problem also (getting a kAudioSessionIncompatibleCategory). What happens if you comment out both these calls?

The reason why the problem occurs when your app in the background, not the foreground is a mystery. You should perhaps track the state (as you seem to be doing with NX_STATE_PLAY), then have two different methods ([self pause] and [self play]), as perhaps the [self pause] (toggling play state) is getting called an unexpected number of times.

jbat100
  • 16,757
  • 4
  • 45
  • 70
  • [self pause] called twice only so basically AudioQueuePause on interruption start and AudioQueueStart on interruption end, I checked it, no problems there. I removed AudioSessionSetActive(FALSE) from my code and I am still getting this things in my log: VERIFY_OSS AudioSessionSetActive(true) - '!cat' VERIFY_OSS AudioQueueStart(audioQueue, NULL) - -12985 – pronebird Nov 04 '11 at 07:21
0

I got it some how work for me, you can try on your risk.

In the function

void _audioSessionInterruptionListener(void *inClientData, UInt32 inInterruptionState)

remove

[(NxAudioStreamer*)inClientData handleInterruptionChangeToState:inInterruptionState];

no need to call handleInterruptionChangeToState when you can directly handle it in audioSessionInterruptionListener because audioSessionInterruptionListener has inInterruptionState as a parameter. So modify your to audioSessionInterruptionListener

void _audioSessionInterruptionListener(void *inClientData, UInt32 inInterruptionState)
{
    if(inInterruptionState == kAudioSessionBeginInterruption)
    {

        NSLog(@"+Interruption"); 

        if(self.state == NX_STATE_PLAY) 
        {
            [self pause];
            AudioSessionSetActive(FALSE);

            isPausedByInterruption = YES;
        }
    }
    else if(inInterruptionState == kAudioSessionEndInterruption) 
    {
        if(isPausedByInterruption) 
        {
            AudioSessionSetActive(TRUE);
            [self pause];

            isPausedByInterruption = FALSE;
        }

        NSLog(@"-Interruption");
    }
}
saadnib
  • 11,145
  • 2
  • 33
  • 54
  • I think I gave up on this and use AVFoundation since iOS 3 is not important for me anymore. Thanks for answer anyway. – pronebird Aug 24 '12 at 12:56