diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c index 6aef52928b..6dab000286 100644 --- a/src/audio/pulseaudio/SDL_pulseaudio.c +++ b/src/audio/pulseaudio/SDL_pulseaudio.c @@ -54,10 +54,12 @@ static SDL_Thread *pulseaudio_hotplug_thread = NULL; static SDL_AtomicInt pulseaudio_hotplug_thread_active; // These are the OS identifiers (i.e. ALSA strings)...these are allocated in a callback -// when the default changes, and claimed/free'd by the hotplug thread when it alerts SDL +// when the default changes, and noticed by the hotplug thread when it alerts SDL // to the change. static char *default_sink_path = NULL; static char *default_source_path = NULL; +static Uint32 default_sink_hash = 0; +static Uint32 default_source_hash = 0; static const char *(*PULSEAUDIO_pa_get_library_version)(void); @@ -804,11 +806,28 @@ static void SourceInfoCallback(pa_context *c, const pa_source_info *i, int is_la static void ServerInfoCallback(pa_context *c, const pa_server_info *i, void *data) { //SDL_Log("PULSEAUDIO ServerInfoCallback!"); - SDL_free(default_sink_path); - default_sink_path = SDL_strdup(i->default_sink_name); - SDL_free(default_source_path); - default_source_path = SDL_strdup(i->default_source_name); + Uint32 hash; // just hash the strings so we can decide if this is different without having to manage a string copy. + + hash = SDL_HashString(i->default_sink_name, NULL); + if (hash != default_sink_hash) { + char *str = SDL_strdup(i->default_sink_name); + if (str) { + SDL_free(default_sink_path); + default_sink_path = str; + default_sink_hash = hash; + } + } + + hash = SDL_HashString(i->default_source_name, NULL); + if (hash != default_source_hash) { + char *str = SDL_strdup(i->default_source_name); + if (str) { + SDL_free(default_source_path); + default_source_path = str; + default_source_hash = hash; + } + } PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); } @@ -857,14 +876,12 @@ static void HotplugCallback(pa_context *c, pa_subscription_event_type_t t, uint3 PULSEAUDIO_pa_threaded_mainloop_signal(pulseaudio_threaded_mainloop, 0); } -static void CheckDefaultDevice(char **pnew_device) +static void CheckDefaultDevice(char *new_device_path, Uint32 *prev_hash, Uint32 new_hash) { - char *new_device = *pnew_device; - if (new_device) { - SDL_AudioDevice *device = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, new_device); + if (new_device_path && (*prev_hash != new_hash)) { + SDL_AudioDevice *device = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, new_device_path); if (device) { // if NULL, we might still be waiting for a SinkInfoCallback or something, we'll try later. - SDL_free(new_device); // done with this, free it. - *pnew_device = NULL; + *prev_hash = new_hash; SDL_DefaultAudioDeviceChanged(device); } } @@ -873,6 +890,8 @@ static void CheckDefaultDevice(char **pnew_device) // this runs as a thread while the Pulse target is initialized to catch hotplug events. static int SDLCALL HotplugThread(void *data) { + Uint32 prev_default_sink_hash = default_sink_hash; + Uint32 prev_default_source_hash = default_source_hash; pa_operation *op; SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW); @@ -892,28 +911,26 @@ static int SDLCALL HotplugThread(void *data) } // Update default devices; don't hold the pulse lock during this, since it could deadlock vs a playing device that we're about to lock here. - char *new_default_sink = default_sink_path; - char *new_default_source = default_source_path; + char *current_default_sink = default_sink_path; + char *current_default_source = default_source_path; default_sink_path = default_source_path = NULL; // make sure we own these before releasing the lock. + const Uint32 current_default_sink_hash = default_sink_hash; + const Uint32 current_default_source_hash = default_source_hash; PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - CheckDefaultDevice(&new_default_sink); - CheckDefaultDevice(&new_default_source); + CheckDefaultDevice(current_default_sink, &prev_default_sink_hash, current_default_sink_hash); + CheckDefaultDevice(current_default_source, &prev_default_source_hash, current_default_source_hash); PULSEAUDIO_pa_threaded_mainloop_lock(pulseaudio_threaded_mainloop); - if (new_default_sink) { // couldn't find this device, try again later. - if (default_sink_path) { - SDL_free(new_default_sink); // uhoh, something else became default while we were unlocked. Dump ours. - } else { - default_sink_path = new_default_sink; // put string back to try again later. - } + if (default_sink_path) { + SDL_free(current_default_sink); // uhoh, something else became default while we were unlocked. Dump ours. + } else { + default_sink_path = current_default_sink; // put string back for later. } - if (new_default_source) { // couldn't find this device, try again later. - if (default_source_path) { - SDL_free(new_default_source); // uhoh, something else became default while we were unlocked. Dump ours. - } else { - default_source_path = new_default_source; // put string back to try again later. - } + if (default_source_path) { + SDL_free(current_default_source); // uhoh, something else became default while we were unlocked. Dump ours. + } else { + default_source_path = current_default_source; // put string back for later. } } @@ -936,17 +953,8 @@ static void PULSEAUDIO_DetectDevices(SDL_AudioDevice **default_output, SDL_Audio WaitForPulseOperation(PULSEAUDIO_pa_context_get_source_info_list(pulseaudio_context, SourceInfoCallback, NULL)); PULSEAUDIO_pa_threaded_mainloop_unlock(pulseaudio_threaded_mainloop); - if (default_sink_path) { - *default_output = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, default_sink_path); - SDL_free(default_sink_path); - default_sink_path = NULL; - } - - if (default_source_path) { - *default_capture = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, default_source_path); - SDL_free(default_source_path); - default_source_path = NULL; - } + *default_output = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, default_sink_path); + *default_capture = SDL_FindPhysicalAudioDeviceByCallback(FindAudioDeviceByPath, default_source_path); // ok, we have a sane list, let's set up hotplug notifications now... SDL_AtomicSet(&pulseaudio_hotplug_thread_active, 1); @@ -980,8 +988,10 @@ static void PULSEAUDIO_Deinitialize(void) SDL_free(default_sink_path); default_sink_path = NULL; + default_sink_hash = 0; SDL_free(default_source_path); default_source_path = NULL; + default_source_hash = 0; UnloadPulseAudioLibrary(); }