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?
Try calling [musicPlayer performSelectorOnMainThread:@selector(play) ...]
instead?
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.
User contributions licensed under CC BY-SA 3.0