Przeglądaj źródła

coreaudio: Always init/deinit session listener on iOS.

Previously, if UpdateAudioSession() failed on close--which it might if
something strange has happened with the system's audio configuration--the
listener wouldn't be deregistered, and would risk touching a free'd pointer
if the app moved to or from the background afterwards, firing an event handler
that should have been deregistered.

Closes #15439.
Ryan C. Gordon 2 tygodni temu
rodzic
commit
a74722ed74
1 zmienionych plików z 37 dodań i 33 usunięć
  1. 37 33
      src/audio/coreaudio/SDL_coreaudio.m

+ 37 - 33
src/audio/coreaudio/SDL_coreaudio.m

@@ -502,39 +502,6 @@ static bool UpdateAudioSession(SDL_AudioDevice *device, bool open, bool allow_pl
             [session setActive:NO error:nil];
             session_active = false;
         }
-
-        if (open) {
-            SDLInterruptionListener *listener = [SDLInterruptionListener new];
-            listener.device = device;
-
-            [center addObserver:listener
-                       selector:@selector(audioSessionInterruption:)
-                           name:AVAudioSessionInterruptionNotification
-                         object:session];
-
-            /* An interruption end notification is not guaranteed to be sent if
-             we were previously interrupted... resuming if needed when the app
-             becomes active seems to be the way to go. */
-            // Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications.
-            [center addObserver:listener
-                       selector:@selector(applicationBecameActive:)
-                           name:UIApplicationDidBecomeActiveNotification
-                         object:nil];
-
-            [center addObserver:listener
-                       selector:@selector(applicationBecameActive:)
-                           name:UIApplicationWillEnterForegroundNotification
-                         object:nil];
-
-            device->hidden->interruption_listener = CFBridgingRetain(listener);
-        } else {
-            SDLInterruptionListener *listener = nil;
-            listener = (SDLInterruptionListener *)CFBridgingRelease(device->hidden->interruption_listener);
-            [center removeObserver:listener];
-            @synchronized(listener) {
-                listener.device = NULL;
-            }
-        }
     }
 
     return true;
@@ -627,6 +594,17 @@ static void COREAUDIO_CloseDevice(SDL_AudioDevice *device)
         return;
     }
 
+    #ifndef MACOSX_COREAUDIO
+    if (device->hidden->interruption_listener) {
+        SDLInterruptionListener *listener = (SDLInterruptionListener *)CFBridgingRelease(device->hidden->interruption_listener);
+        device->hidden->interruption_listener = nil;
+        [center removeObserver:listener];
+        @synchronized(listener) {
+            listener.device = NULL;
+        }
+    }
+    #endif
+
     // dispose of the audio queue before waiting on the thread, or it might stall for a long time!
     if (device->hidden->audioQueue) {
         AudioQueueFlush(device->hidden->audioQueue);
@@ -998,6 +976,32 @@ static bool COREAUDIO_OpenDevice(SDL_AudioDevice *device)
         return SDL_SetError("%s", device->hidden->thread_error);
     }
 
+#ifndef MACOSX_COREAUDIO
+    SDLInterruptionListener *listener = [SDLInterruptionListener new];
+    listener.device = device;
+
+    [center addObserver:listener
+               selector:@selector(audioSessionInterruption:)
+                   name:AVAudioSessionInterruptionNotification
+                 object:session];
+
+    /* An interruption end notification is not guaranteed to be sent if
+       we were previously interrupted... resuming if needed when the app
+       becomes active seems to be the way to go. */
+    // Note: object: below needs to be nil, as otherwise it filters by the object, and session doesn't send foreground / active notifications.
+    [center addObserver:listener
+               selector:@selector(applicationBecameActive:)
+                   name:UIApplicationDidBecomeActiveNotification
+                 object:nil];
+
+    [center addObserver:listener
+               selector:@selector(applicationBecameActive:)
+                   name:UIApplicationWillEnterForegroundNotification
+                 object:nil];
+
+    device->hidden->interruption_listener = CFBridgingRetain(listener);
+#endif
+
     return (device->hidden->thread != NULL);
 }