How Can I Solve An EXC_BAD_ACCESS That Occurs Within the AudioToolBox of IOS

2

I need help solving a problem where I experience an occasional EXC_BAD_ACCESS crash that occurs in the AudioToolBox (AVAudioSessionPropertyListener) during transition between MPMoviePlayerController and MPMusicPlayerController. The following is the call trace:

OS Version:      iPhone OS 5.1 (9B176)
Report Version:  104

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x00000009
Crashed Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libobjc.A.dylib                 0x3676ef78 objc_msgSend + 16
1   AVFoundation                    0x31e60b1c _ZL30AVAudioSessionPropertyListenerPvmmPKv + 236
2   AudioToolbox                    0x3630b300 AudioSessionPropertyListeners::CallPropertyListenersImp(unsigned long, unsigned long, void const*) + 268
3   AudioToolbox                    0x3630b5de AudioSessionPropertyListeners::CallPropertyListeners(unsigned long, unsigned long, void const*) + 234
4   AudioToolbox                    0x3630925a SSServer_AudioSessionInterruptionListenerMessage + 50
5   AudioToolbox                    0x362b0d2c _XAudioSessionInterruptionListenerMessage + 56
6   AudioToolbox                    0x36245cdc mshMIGPerform + 368
7   CoreFoundation                  0x3532151c __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 32
8   CoreFoundation                  0x353214be __CFRunLoopDoSource1 + 134
9   CoreFoundation                  0x3532030c __CFRunLoopRun + 1364
10  CoreFoundation                  0x352a349e CFRunLoopRunSpecific + 294
11  CoreFoundation                  0x352a3366 CFRunLoopRunInMode + 98
12  GraphicsServices                0x3659f432 GSEventRunModal + 130
13  UIKit                           0x32399e76 UIApplicationMain + 1074
14  MyGreatApp                      0x00054986 main (main.m:14)
15  MyGreatApp                      0x00054944 start + 32

My app alternates the use of the music player and the movie player. The movie player is used for playing podcasts. The movie player is allocated with an autorelease each time a new podcast is played. When I am done with the movie player, I remove any observers that I have set up, make sure the movie player is stopped, and restart the music player.

Here is some relevant code to initialize the use of the movie player:

[musicPlayer pause];

self.moviePlayer = [[[MPMoviePlayerController alloc] initWithContentURL: address] autorelease];   // Release the old moviePlayer and create a new one.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePreloadDidFinish:) name:MPMoviePlayerLoadStateDidChangeNotification object:self.moviePlayer];    

// Register to receive a notification when the movie has finished playing. 
[[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(moviePlayBackDidFinish:) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer];

// Register to receive a notification when the movie playback state changes (specifically looking for the state of interrupted). 
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moviePlayBackStateChanged:) name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.moviePlayer];

[self.moviePlayer play];

The movie can either finish after it plays out, or the user can terminate the movie by pressing a fast forward button. Here is the relevant code for handling these two scenarios:

-(IBAction) fastForwardMovie: (id) sender {

        // The self.moviePlayer is still playing, but the user wants to skip the rest of the movie/podcast. 
        // Lets fake a movie playback did finish event
        NSNotification *notification = [NSNotification notificationWithName:MPMoviePlayerPlaybackDidFinishNotification object:nil];
        [self moviePlayBackDidFinish: notification];    // Fake a finished playback so that the music player can be restarted
        return;
}


//  Notification called when the movie FINISHED playing.
- (void) moviePlayBackDidFinish:(NSNotification*)notification { 
    // Remove further notifications until the next time we need the movie player
    [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerLoadStateDidChangeNotification object:self.moviePlayer] ;
    [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer] ;
    [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackStateDidChangeNotification object:self.moviePlayer] ;


    // Get rid of the movie player
    self.moviePlayer.initialPlaybackTime = -1;  // seems that the best way to kill the movie player is first setting the playback time to -1
    [self.moviePlayer stop];                    // followed by stopping the player

    [musicPlayer play];
}

After the movie (podcast) finishes, music will play even when the crash occurs (but the app is terminated). This is because I am using the iPod music player, but this may suggest that the problem is associated with the movie player.

One last note. I have tried to carefully follow the guidelines of Apple's Audio Session Programming Guide where it says that when the desired configuration is to have all audio mix, you should "Configure an audio session using a mixable category configuration," and "Take advantage of the movie player’s default useApplicationAudioSession value of YES".

Can anyone help me figure out what I might be doing wrong that is resulting in the AVAudioSessionPropertyListener to crash with the EXC_BAD_ACCESS? Or at least can you give me a suggestion on how to isolate this problem to determine the root cause?

ios
audio
memory-management
mpmovieplayercontroller
mpmusicplayercontroller
asked on Stack Overflow Apr 22, 2012 by JeffB6688 • edited Jun 12, 2012 by leppie

2 Answers

1

Try calling [musicPlayer performSelectorOnMainThread:@selector(play) ...] instead?

answered on Stack Overflow Jun 12, 2012 by user1330054 • edited Jun 12, 2012 by bool.dev
0

We've just been tracking a very similar crash.

Ours turn out to be as described here:

https://github.com/mattgallagher/AudioStreamer/issues/6

In particular, MyAudioSessionInterruptionListener (or the name of the callback passed to AudioSessionInitialize) and it's inClientData can not be changed after it's been registered, so the callback must always do something sensible even if the underlying object has been deallocated.

The solution suggested for AudioStream is to use a static variable, and make sure it points to the object that is currently interested in the callback, and never points at a deallocated object - the important thing is not to use inClientData.

answered on Stack Overflow Dec 4, 2012 by JosephH

User contributions licensed under CC BY-SA 3.0