Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 24 additions & 7 deletions src/audio/coreaudio/SDL_coreaudio.m
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ - (void)audioSessionInterruption:(NSNotification *)note
{
@synchronized(self) {
NSNumber *type = note.userInfo[AVAudioSessionInterruptionTypeKey];
/* Defensive: skip if the device was already torn down. */
if (self.device == NULL) {
return;
}
if (type.unsignedIntegerValue == AVAudioSessionInterruptionTypeBegan) {
interruption_begin(self.device);
} else {
Expand All @@ -363,6 +367,9 @@ - (void)audioSessionInterruption:(NSNotification *)note
- (void)applicationBecameActive:(NSNotification *)note
{
@synchronized(self) {
if (self.device == NULL) {
return;
}
interruption_end(self.device);
}
}
Expand All @@ -381,6 +388,23 @@ static BOOL update_audio_session(_THIS, SDL_bool open, SDL_bool allow_playandrec
NSError *err = nil;
const char *hint;

/* Close path: unregister the interruption observer FIRST, before any
code that can early-return. [AVAudioSession setCategory:] failures
(phone call, Siri, CarPlay / AirPlay handoff, etc.) would otherwise
leave the listener registered with a stale device pointer;
NotificationCenter later delivers foreground events to freed memory.
See #2900 for the sibling setActive:YES leak fix; this closes the
setCategory: leak. */
if (!open && this->hidden->interruption_listener != NULL) {
SDLInterruptionListener *listener =
(SDLInterruptionListener *)CFBridgingRelease(this->hidden->interruption_listener);
this->hidden->interruption_listener = NULL;
[center removeObserver:listener];
@synchronized(listener) {
listener.device = NULL;
}
}

hint = SDL_GetHint(SDL_HINT_AUDIO_CATEGORY);
if (hint) {
if (SDL_strcasecmp(hint, "AVAudioSessionCategoryAmbient") == 0 ||
Expand Down Expand Up @@ -498,13 +522,6 @@ static BOOL update_audio_session(_THIS, SDL_bool open, SDL_bool allow_playandrec
object:nil];

this->hidden->interruption_listener = CFBridgingRetain(listener);
} else {
SDLInterruptionListener *listener = nil;
listener = (SDLInterruptionListener *)CFBridgingRelease(this->hidden->interruption_listener);
[center removeObserver:listener];
@synchronized(listener) {
listener.device = NULL;
}
}
}

Expand Down
Loading