diff -r 375799828431 src/audio/pulseaudio/SDL_pulseaudio.c --- a/src/audio/pulseaudio/SDL_pulseaudio.c Sat Feb 21 00:33:25 2015 -0500 +++ b/src/audio/pulseaudio/SDL_pulseaudio.c Sat Feb 21 14:50:55 2015 +0100 @@ -65,6 +65,13 @@ #endif /* pulseaudio <= 0.9.10 */ +typedef struct +{ + uint8_t last; + SDL_AddAudioDevice addfn; +} sink_struct; + + static const char *(*PULSEAUDIO_pa_get_library_version) (void); static pa_simple *(*PULSEAUDIO_pa_simple_new) (const char *, const char *, pa_stream_direction_t, const char *, const char *, const pa_sample_spec *, @@ -106,12 +113,14 @@ static int load_pulseaudio_syms(void); +pa_operation* (*PULSEAUDIO_pa_context_get_sink_info_list)(pa_context *c, pa_sink_info_cb_t cb, void *userdata); #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC static const char *pulseaudio_library = SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC; static void *pulseaudio_handle = NULL; + static int load_pulseaudio_sym(const char *fn, void **addr) { @@ -202,11 +211,30 @@ SDL_PULSEAUDIO_SYM(pa_stream_disconnect); SDL_PULSEAUDIO_SYM(pa_stream_unref); SDL_PULSEAUDIO_SYM(pa_channel_map_init_auto); + SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list); SDL_PULSEAUDIO_SYM(pa_strerror); return 0; } +static void +DeInitializePulseAudio(struct SDL_PrivateAudioData *pulse_local_server) +{ + if (pulse_local_server != NULL) { + if (pulse_local_server->context != NULL) { + PULSEAUDIO_pa_context_disconnect(pulse_local_server->context); + PULSEAUDIO_pa_context_unref(pulse_local_server->context); + pulse_local_server->context = NULL; + } + if (pulse_local_server->mainloop != NULL) { + PULSEAUDIO_pa_mainloop_free(pulse_local_server->mainloop); + pulse_local_server->mainloop = NULL; + } + SDL_free(pulse_local_server); + } +} + + /* Check to see if we can connect to PulseAudio */ static SDL_bool CheckPulseAudioAvailable() @@ -307,16 +335,7 @@ PULSEAUDIO_pa_stream_unref(this->hidden->stream); this->hidden->stream = NULL; } - if (this->hidden->context != NULL) { - PULSEAUDIO_pa_context_disconnect(this->hidden->context); - PULSEAUDIO_pa_context_unref(this->hidden->context); - this->hidden->context = NULL; - } - if (this->hidden->mainloop != NULL) { - PULSEAUDIO_pa_mainloop_free(this->hidden->mainloop); - this->hidden->mainloop = NULL; - } - SDL_free(this->hidden); + DeInitializePulseAudio(this->hidden); this->hidden = NULL; } } @@ -344,6 +363,61 @@ return "SDL Application"; /* oh well. */ } + +static struct SDL_PrivateAudioData * +InitializePulseAudio(int *error) +{ + struct SDL_PrivateAudioData *h = NULL; + int state = 0; + error = 0; + + struct SDL_PrivateAudioData *pulse_local_server = SDL_malloc((sizeof *pulse_local_server)); + + if (pulse_local_server == NULL) { + *error = SDL_OutOfMemory(); + return 0; + } + SDL_memset(pulse_local_server, 0, (sizeof *pulse_local_server)); + h = pulse_local_server; + + /* Set up a new main loop */ + if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) { + DeInitializePulseAudio(pulse_local_server); + *error = SDL_SetError("pa_mainloop_new() failed"); + return 0; + } + + h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop); + h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, getAppName()); + if (!h->context) { + DeInitializePulseAudio(pulse_local_server); + *error = SDL_SetError("pa_context_new() failed"); + return 0; + } + + /* Connect to the PulseAudio server */ + if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) { + DeInitializePulseAudio(pulse_local_server); + *error = SDL_SetError("Could not setup connection to PulseAudio"); + return 0; + } + + do { + if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { + DeInitializePulseAudio(pulse_local_server); + *error = SDL_SetError("pa_mainloop_iterate() failed"); + return 0; + } + state = PULSEAUDIO_pa_context_get_state(h->context); + if (!PA_CONTEXT_IS_GOOD(state)) { + DeInitializePulseAudio(pulse_local_server); + *error = SDL_SetError("Could not connect to PulseAudio"); + return 0; + } + } while (state != PA_CONTEXT_READY); + return pulse_local_server; +} + static int PULSEAUDIO_OpenDevice(_THIS, const char *devname, int iscapture) { @@ -354,14 +428,15 @@ pa_channel_map pacmap; pa_stream_flags_t flags = 0; int state = 0; + int error = 0; /* Initialize all variables that we clean on shutdown */ - this->hidden = (struct SDL_PrivateAudioData *) - SDL_malloc((sizeof *this->hidden)); + this->hidden = InitializePulseAudio(&error); + if (this->hidden == NULL) { - return SDL_OutOfMemory(); + return error; } - SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + h = this->hidden; paspec.format = PA_SAMPLE_INVALID; @@ -448,36 +523,6 @@ PA_CHANNEL_MAP_WAVEEX); /* Set up a new main loop */ - if (!(h->mainloop = PULSEAUDIO_pa_mainloop_new())) { - PULSEAUDIO_CloseDevice(this); - return SDL_SetError("pa_mainloop_new() failed"); - } - - h->mainloop_api = PULSEAUDIO_pa_mainloop_get_api(h->mainloop); - h->context = PULSEAUDIO_pa_context_new(h->mainloop_api, getAppName()); - if (!h->context) { - PULSEAUDIO_CloseDevice(this); - return SDL_SetError("pa_context_new() failed"); - } - - /* Connect to the PulseAudio server */ - if (PULSEAUDIO_pa_context_connect(h->context, NULL, 0, NULL) < 0) { - PULSEAUDIO_CloseDevice(this); - return SDL_SetError("Could not setup connection to PulseAudio"); - } - - do { - if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { - PULSEAUDIO_CloseDevice(this); - return SDL_SetError("pa_mainloop_iterate() failed"); - } - state = PULSEAUDIO_pa_context_get_state(h->context); - if (!PA_CONTEXT_IS_GOOD(state)) { - PULSEAUDIO_CloseDevice(this); - return SDL_SetError("Could not connect to PulseAudio"); - } - } while (state != PA_CONTEXT_READY); - h->stream = PULSEAUDIO_pa_stream_new( h->context, "Simple DirectMedia Layer", /* stream description */ @@ -490,7 +535,7 @@ return SDL_SetError("Could not set up PulseAudio stream"); } - if (PULSEAUDIO_pa_stream_connect_playback(h->stream, NULL, &paattr, flags, + if (PULSEAUDIO_pa_stream_connect_playback(h->stream, devname, &paattr, flags, NULL, NULL) < 0) { PULSEAUDIO_CloseDevice(this); return SDL_SetError("Could not connect PulseAudio stream"); @@ -519,6 +564,52 @@ UnloadPulseAudioLibrary(); } + +static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) +{ + sink_struct *a = (sink_struct *) userdata; + + a->last = is_last; + if (i) { + a->addfn(i->name); + } +} + +static void +PULSEAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn) +{ + struct SDL_PrivateAudioData *h = NULL; + struct SDL_PrivateAudioData *pulse_local_server = NULL; + int error = 0; //This Value is ignored at the moment, used only for InitializePulseAudio + + pulse_local_server = InitializePulseAudio(&error); + + if (pulse_local_server == NULL) { + //If there is a way to clear the error, this should probably be done here + //to prevent possibly wrong error message + return; + } + + sink_struct a; + h = pulse_local_server; + + if (iscapture != 1) { + a.last = 0; + a.addfn = addfn; + pa_operation* o = PULSEAUDIO_pa_context_get_sink_info_list(h->context, + get_sink_info_callback, &a); + while (!a.last) { + if (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_CANCELLED) { + break; + } + if (PULSEAUDIO_pa_mainloop_iterate(h->mainloop, 1, NULL) < 0) { + break; + } + } + } + DeInitializePulseAudio(pulse_local_server); +} + static int PULSEAUDIO_Init(SDL_AudioDriverImpl * impl) { @@ -530,8 +621,9 @@ UnloadPulseAudioLibrary(); return 0; } - + /* Set the function pointers */ + impl->DetectDevices = PULSEAUDIO_DetectDevices; impl->OpenDevice = PULSEAUDIO_OpenDevice; impl->PlayDevice = PULSEAUDIO_PlayDevice; impl->WaitDevice = PULSEAUDIO_WaitDevice; @@ -539,7 +631,7 @@ impl->CloseDevice = PULSEAUDIO_CloseDevice; impl->WaitDone = PULSEAUDIO_WaitDone; impl->Deinitialize = PULSEAUDIO_Deinitialize; - impl->OnlyHasDefaultOutputDevice = 1; + impl->OnlyHasDefaultOutputDevice = 0; return 1; /* this audio target is available. */ }