Obscure YouTube player bugs on the iPhone
While debugging our new Tickle Tap Toddler Pack iPhone app, I had to fix a couple of obscure bugs caused by the YouTube video on the app’s info screen. After watching the video, none of the sound effects in Sound Shaker work and Field Flier just displays a blank white screen… very strange. After a bunch of digging around, it turns out that the YouTube video player isn’t a very good iPhone citizen. Oh sure, it’s very friendly and triggers all of the events and callbacks you’d expect when it opens. But when it closes, it doesn’t trigger anything, leaving the app wondering what the heck is going on.
The YouTube player breaks Sound Shaker because the app recieves an audio interruption event (kAudioSessionBeginInterruption) when the YouTube player opens, but never receives a corresponding event (kAudioSessionEndInterruption) when the player closes. Being a good citizen, the app deactivates it’s audio session (and suspends it’s OpenAL context) when the interruption begins, but the poor little guy never gets a chance to reactivate it again. Not cool.
The YouTube player breaks Field Flier because it creates a new key window when it opens and sets the key window to nil when it closes. When Field Flier goes looking for the key window, it’s nowhere to be found. Oh sure, the app could listen for a UIWindowDidResignKeyNotification notification to reset the key window – too bad the YouTube player doesn’t trigger that particular notification. I’m pretty sure the YouTube player has it’s thumbs in it’s ears going na nana nana na at this point. Again, not cool.
The hacktastic solution to both issues is to listen for a UIWindowDidBecomeVisibleNotification notification on the app’s main window and manually call the interruption listener callback and reset the key window. I put the relevant code in the app delegate, but if your app shows and hides the main window for some other reason, you’ll probably need to get a bit fancier.
Here’s the relevant code in TickleTapToddlerPackAppDelegate.m:
void interruptionListenerCallback (void *inUserData, UInt32 interruptionState ) {
if(interruptionState == kAudioSessionBeginInterruption) {
AudioSessionSetActive(NO);
// suspend OpenAL context here...
} else if(interruptionState == kAudioSessionEndInterruption) {
UInt32 category = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
AudioSessionSetActive(YES);
// resume OpenAL context here...
}
}
- (void) applicationDidFinishLaunching: (UIApplication*) application {
// initialize the audio session and register the interruption listener callback
AudioSessionInitialize(NULL, NULL, interruptionListenerCallback, self);
UInt32 category = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
}
- (void)didReceiveWindowDidBecomeVisibleNotification:(NSNotification *)notification {
// manually call the interruption listener callback
interruptionListenerCallback((void *)self, kAudioSessionEndInterruption);
// manually set the key window
[self.window makeKeyWindow];
}