diff options
Diffstat (limited to 'media/libcubeb/src/cubeb_pulse.c')
-rw-r--r-- | media/libcubeb/src/cubeb_pulse.c | 488 |
1 files changed, 129 insertions, 359 deletions
diff --git a/media/libcubeb/src/cubeb_pulse.c b/media/libcubeb/src/cubeb_pulse.c index 846ba426a..4f474452d 100644 --- a/media/libcubeb/src/cubeb_pulse.c +++ b/media/libcubeb/src/cubeb_pulse.c @@ -7,14 +7,12 @@ #undef NDEBUG #include <assert.h> #include <dlfcn.h> -#include <pulse/pulseaudio.h> -#include <stdio.h> #include <stdlib.h> +#include <pulse/pulseaudio.h> #include <string.h> -#include "cubeb-internal.h" #include "cubeb/cubeb.h" -#include "cubeb_mixer.h" -#include "cubeb_strings.h" +#include "cubeb-internal.h" +#include <stdio.h> #ifdef DISABLE_LIBPULSE_DLOPEN #define WRAP(x) x @@ -22,14 +20,13 @@ #define WRAP(x) cubeb_##x #define LIBPULSE_API_VISIT(X) \ X(pa_channel_map_can_balance) \ - X(pa_channel_map_init) \ + X(pa_channel_map_init_auto) \ X(pa_context_connect) \ X(pa_context_disconnect) \ X(pa_context_drain) \ X(pa_context_get_server_info) \ X(pa_context_get_sink_info_by_name) \ X(pa_context_get_sink_info_list) \ - X(pa_context_get_sink_input_info) \ X(pa_context_get_source_info_list) \ X(pa_context_get_state) \ X(pa_context_new) \ @@ -84,50 +81,33 @@ X(pa_context_set_subscribe_callback) \ X(pa_context_subscribe) \ X(pa_mainloop_api_once) \ - X(pa_get_library_version) \ - X(pa_channel_map_init_auto) \ #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x; LIBPULSE_API_VISIT(MAKE_TYPEDEF); #undef MAKE_TYPEDEF #endif -#if PA_CHECK_VERSION(2, 0, 0) -static int has_pulse_v2 = 0; -#endif - static struct cubeb_ops const pulse_ops; -struct cubeb_default_sink_info { - pa_channel_map channel_map; - uint32_t sample_spec_rate; - pa_sink_flags_t flags; -}; - struct cubeb { struct cubeb_ops const * ops; void * libpulse; pa_threaded_mainloop * mainloop; pa_context * context; - struct cubeb_default_sink_info * default_sink_info; + pa_sink_info * default_sink_info; char * context_name; int error; - cubeb_device_collection_changed_callback output_collection_changed_callback; - void * output_collection_changed_user_ptr; - cubeb_device_collection_changed_callback input_collection_changed_callback; - void * input_collection_changed_user_ptr; - cubeb_strings * device_ids; + cubeb_device_collection_changed_callback collection_changed_callback; + void * collection_changed_user_ptr; }; struct cubeb_stream { - /* Note: Must match cubeb_stream layout in cubeb.c. */ cubeb * context; - void * user_ptr; - /**/ pa_stream * output_stream; pa_stream * input_stream; cubeb_data_callback data_callback; cubeb_state_callback state_callback; + void * user_ptr; pa_time_event * drain_timer; pa_sample_spec output_sample_spec; pa_sample_spec input_sample_spec; @@ -144,24 +124,6 @@ enum cork_state { NOTIFY = 1 << 1 }; -static int -intern_device_id(cubeb * ctx, char const ** id) -{ - char const * interned; - - assert(ctx); - assert(id); - - interned = cubeb_strings_intern(ctx->device_ids, *id); - if (!interned) { - return CUBEB_ERROR; - } - - *id = interned; - - return CUBEB_OK; -} - static void sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u) { @@ -169,10 +131,8 @@ sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, voi cubeb * ctx = u; if (!eol) { free(ctx->default_sink_info); - ctx->default_sink_info = malloc(sizeof(struct cubeb_default_sink_info)); - memcpy(&ctx->default_sink_info->channel_map, &info->channel_map, sizeof(pa_channel_map)); - ctx->default_sink_info->sample_spec_rate = info->sample_spec.rate; - ctx->default_sink_info->flags = info->flags; + ctx->default_sink_info = malloc(sizeof(pa_sink_info)); + memcpy(ctx->default_sink_info, info, sizeof(pa_sink_info)); } WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); } @@ -180,11 +140,7 @@ sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, voi static void server_info_callback(pa_context * context, const pa_server_info * info, void * u) { - pa_operation * o; - o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, sink_info_callback, u); - if (o) { - WRAP(pa_operation_unref)(o); - } + WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, sink_info_callback, u); } static void @@ -511,81 +467,12 @@ stream_update_timing_info(cubeb_stream * stm) return r; } -static pa_channel_position_t -cubeb_channel_to_pa_channel(cubeb_channel channel) -{ - switch (channel) { - case CHANNEL_FRONT_LEFT: - return PA_CHANNEL_POSITION_FRONT_LEFT; - case CHANNEL_FRONT_RIGHT: - return PA_CHANNEL_POSITION_FRONT_RIGHT; - case CHANNEL_FRONT_CENTER: - return PA_CHANNEL_POSITION_FRONT_CENTER; - case CHANNEL_LOW_FREQUENCY: - return PA_CHANNEL_POSITION_LFE; - case CHANNEL_BACK_LEFT: - return PA_CHANNEL_POSITION_REAR_LEFT; - case CHANNEL_BACK_RIGHT: - return PA_CHANNEL_POSITION_REAR_RIGHT; - case CHANNEL_FRONT_LEFT_OF_CENTER: - return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; - case CHANNEL_FRONT_RIGHT_OF_CENTER: - return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; - case CHANNEL_BACK_CENTER: - return PA_CHANNEL_POSITION_REAR_CENTER; - case CHANNEL_SIDE_LEFT: - return PA_CHANNEL_POSITION_SIDE_LEFT; - case CHANNEL_SIDE_RIGHT: - return PA_CHANNEL_POSITION_SIDE_RIGHT; - case CHANNEL_TOP_CENTER: - return PA_CHANNEL_POSITION_TOP_CENTER; - case CHANNEL_TOP_FRONT_LEFT: - return PA_CHANNEL_POSITION_TOP_FRONT_LEFT; - case CHANNEL_TOP_FRONT_CENTER: - return PA_CHANNEL_POSITION_TOP_FRONT_CENTER; - case CHANNEL_TOP_FRONT_RIGHT: - return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; - case CHANNEL_TOP_BACK_LEFT: - return PA_CHANNEL_POSITION_TOP_REAR_LEFT; - case CHANNEL_TOP_BACK_CENTER: - return PA_CHANNEL_POSITION_TOP_REAR_CENTER; - case CHANNEL_TOP_BACK_RIGHT: - return PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - default: - return PA_CHANNEL_POSITION_INVALID; - } -} - -static void -layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm) -{ - assert(cm && layout != CUBEB_LAYOUT_UNDEFINED); - - WRAP(pa_channel_map_init)(cm); - - uint32_t channels = 0; - cubeb_channel_layout channelMap = layout; - for (uint32_t i = 0 ; channelMap != 0; ++i) { - uint32_t channel = (channelMap & 1) << i; - if (channel != 0) { - cm->map[channels] = cubeb_channel_to_pa_channel(channel); - channels++; - } - channelMap = channelMap >> 1; - } - unsigned int channels_from_layout = cubeb_channel_layout_nb_channels(layout); - assert(channels_from_layout <= UINT8_MAX); - cm->channels = (uint8_t) channels_from_layout; -} - static void pulse_context_destroy(cubeb * ctx); static void pulse_destroy(cubeb * ctx); static int pulse_context_init(cubeb * ctx) { - int r; - if (ctx->context) { assert(ctx->error == 1); pulse_context_destroy(ctx); @@ -599,9 +486,9 @@ pulse_context_init(cubeb * ctx) WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx); WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); - r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL); + WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL); - if (r < 0 || wait_until_context_ready(ctx) != 0) { + if (wait_until_context_ready(ctx) != 0) { WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); pulse_context_destroy(ctx); ctx->context = NULL; @@ -615,25 +502,18 @@ pulse_context_init(cubeb * ctx) return 0; } -static int pulse_subscribe_notifications(cubeb * context, - pa_subscription_mask_t mask); - /*static*/ int pulse_init(cubeb ** context, char const * context_name) { void * libpulse = NULL; cubeb * ctx; - pa_operation * o; *context = NULL; #ifndef DISABLE_LIBPULSE_DLOPEN libpulse = dlopen("libpulse.so.0", RTLD_LAZY); if (!libpulse) { - libpulse = dlopen("libpulse.so", RTLD_LAZY); - if (!libpulse) { - return CUBEB_ERROR; - } + return CUBEB_ERROR; } #define LOAD(x) { \ @@ -648,20 +528,11 @@ pulse_init(cubeb ** context, char const * context_name) #undef LOAD #endif -#if PA_CHECK_VERSION(2, 0, 0) - const char* version = WRAP(pa_get_library_version)(); - has_pulse_v2 = strtol(version, NULL, 10) >= 2; -#endif - ctx = calloc(1, sizeof(*ctx)); assert(ctx); ctx->ops = &pulse_ops; ctx->libpulse = libpulse; - if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) { - pulse_destroy(ctx); - return CUBEB_ERROR; - } ctx->mainloop = WRAP(pa_threaded_mainloop_new)(); ctx->default_sink_info = NULL; @@ -674,20 +545,10 @@ pulse_init(cubeb ** context, char const * context_name) return CUBEB_ERROR; } - /* server_info_callback performs a second async query, which is - responsible for initializing default_sink_info and signalling the - mainloop to end the wait. */ WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); - o = WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx); - if (o) { - operation_wait(ctx, NULL, o); - WRAP(pa_operation_unref)(o); - } + WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx); WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); - /* Update `default_sink_info` when the default device changes. */ - pulse_subscribe_notifications(ctx, PA_SUBSCRIPTION_MASK_SERVER); - *context = ctx; return CUBEB_OK; @@ -706,8 +567,11 @@ pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) (void)ctx; assert(ctx && max_channels); - if (!ctx->default_sink_info) - return CUBEB_ERROR; + WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); + while (!ctx->default_sink_info) { + WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); + } + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); *max_channels = ctx->default_sink_info->channel_map.channels; @@ -720,10 +584,13 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) assert(ctx && rate); (void)ctx; - if (!ctx->default_sink_info) - return CUBEB_ERROR; + WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); + while (!ctx->default_sink_info) { + WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); + } + WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); - *rate = ctx->default_sink_info->sample_spec_rate; + *rate = ctx->default_sink_info->sample_spec.rate; return CUBEB_OK; } @@ -758,7 +625,9 @@ pulse_context_destroy(cubeb * ctx) static void pulse_destroy(cubeb * ctx) { - free(ctx->context_name); + if (ctx->context_name) { + free(ctx->context_name); + } if (ctx->context) { pulse_context_destroy(ctx); } @@ -768,14 +637,12 @@ pulse_destroy(cubeb * ctx) WRAP(pa_threaded_mainloop_free)(ctx->mainloop); } - if (ctx->device_ids) { - cubeb_strings_destroy(ctx->device_ids); - } - if (ctx->libpulse) { dlclose(ctx->libpulse); } - free(ctx->default_sink_info); + if (ctx->default_sink_info) { + free(ctx->default_sink_info); + } free(ctx); } @@ -798,25 +665,6 @@ to_pulse_format(cubeb_sample_format format) } } -static cubeb_channel_layout -pulse_default_layout_for_channels(uint32_t ch) -{ - assert (ch > 0 && ch <= 8); - switch (ch) { - case 1: return CUBEB_LAYOUT_MONO; - case 2: return CUBEB_LAYOUT_STEREO; - case 3: return CUBEB_LAYOUT_3F; - case 4: return CUBEB_LAYOUT_QUAD; - case 5: return CUBEB_LAYOUT_3F2; - case 6: return CUBEB_LAYOUT_3F_LFE | - CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT; - case 7: return CUBEB_LAYOUT_3F3R_LFE; - case 8: return CUBEB_LAYOUT_3F4_LFE; - } - // Never get here! - return CUBEB_LAYOUT_UNDEFINED; -} - static int create_pa_stream(cubeb_stream * stm, pa_stream ** pa_stm, @@ -824,39 +672,15 @@ create_pa_stream(cubeb_stream * stm, char const * stream_name) { assert(stm && stream_params); - assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm && - (stream_params->layout == CUBEB_LAYOUT_UNDEFINED || - (stream_params->layout != CUBEB_LAYOUT_UNDEFINED && - cubeb_channel_layout_nb_channels(stream_params->layout) == stream_params->channels)))); - if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { - return CUBEB_ERROR_NOT_SUPPORTED; - } *pa_stm = NULL; pa_sample_spec ss; ss.format = to_pulse_format(stream_params->format); if (ss.format == PA_SAMPLE_INVALID) return CUBEB_ERROR_INVALID_FORMAT; ss.rate = stream_params->rate; - if (stream_params->channels > UINT8_MAX) - return CUBEB_ERROR_INVALID_FORMAT; - ss.channels = (uint8_t) stream_params->channels; - - if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) { - pa_channel_map cm; - if (stream_params->channels <= 8 && - !WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels, PA_CHANNEL_MAP_DEFAULT)) { - LOG("Layout undefined and PulseAudio's default layout has not been configured, guess one."); - layout_to_channel_map(pulse_default_layout_for_channels(stream_params->channels), &cm); - *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm); - } else { - LOG("Layout undefined, PulseAudio will use its default."); - *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL); - } - } else { - pa_channel_map cm; - layout_to_channel_map(stream_params->layout, &cm); - *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm); - } + ss.channels = stream_params->channels; + + *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL); return (*pa_stm == NULL) ? CUBEB_ERROR : CUBEB_OK; } @@ -929,7 +753,7 @@ pulse_stream_init(cubeb * context, battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec); WRAP(pa_stream_connect_playback)(stm->output_stream, - (char const *) output_device, + output_device, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY, @@ -952,7 +776,7 @@ pulse_stream_init(cubeb * context, battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec); WRAP(pa_stream_connect_record)(stm->input_stream, - (char const *) input_device, + input_device, &battr, PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY); @@ -972,7 +796,7 @@ pulse_stream_init(cubeb * context, return CUBEB_ERROR; } - if (g_cubeb_log_level) { + if (g_log_level) { if (output_stream_params){ const pa_buffer_attr * output_att; output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream); @@ -989,7 +813,6 @@ pulse_stream_init(cubeb * context, } *stream = stm; - LOG("Cubeb stream (%p) init successful.", *stream); return CUBEB_OK; } @@ -1021,7 +844,6 @@ pulse_stream_destroy(cubeb_stream * stm) } WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); - LOG("Cubeb stream (%p) destroyed successfully.", stm); free(stm); } @@ -1053,7 +875,6 @@ pulse_stream_start(cubeb_stream * stm) WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); } - LOG("Cubeb stream (%p) started successfully.", stm); return CUBEB_OK; } @@ -1069,7 +890,6 @@ pulse_stream_stop(cubeb_stream * stm) WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); stream_cork(stm, CORK | NOTIFY); - LOG("Cubeb stream (%p) stopped successfully.", stm); return CUBEB_OK; } @@ -1142,7 +962,6 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume) pa_volume_t vol; pa_cvolume cvol; const pa_sample_spec * ss; - cubeb * ctx; if (!stm->output_stream) { return CUBEB_ERROR; @@ -1150,11 +969,13 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume) WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); + while (!stm->context->default_sink_info) { + WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop); + } + /* if the pulse daemon is configured to use flat volumes, * apply our own gain instead of changing the input volume on the sink. */ - ctx = stm->context; - if (ctx->default_sink_info && - (ctx->default_sink_info->flags & PA_SINK_FLAT_VOLUME)) { + if (stm->context->default_sink_info->flags & PA_SINK_FLAT_VOLUME) { stm->volume = volume; } else { ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream); @@ -1164,40 +985,46 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume) index = WRAP(pa_stream_get_index)(stm->output_stream); - op = WRAP(pa_context_set_sink_input_volume)(ctx->context, + op = WRAP(pa_context_set_sink_input_volume)(stm->context->context, index, &cvol, volume_success, stm); if (op) { - operation_wait(ctx, stm->output_stream, op); + operation_wait(stm->context, stm->output_stream, op); WRAP(pa_operation_unref)(op); } } - WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); + WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); return CUBEB_OK; } -struct sink_input_info_result { - pa_cvolume * cvol; - pa_threaded_mainloop * mainloop; -}; - -static void -sink_input_info_cb(pa_context * c, pa_sink_input_info const * i, int eol, void * u) +static int +pulse_stream_set_panning(cubeb_stream * stream, float panning) { - struct sink_input_info_result * r = u; - if (!eol) { - *r->cvol = i->volume; + const pa_channel_map * map; + pa_cvolume vol; + + if (!stream->output_stream) { + return CUBEB_ERROR; } - WRAP(pa_threaded_mainloop_signal)(r->mainloop, 0); + + map = WRAP(pa_stream_get_channel_map)(stream->output_stream); + + if (!WRAP(pa_channel_map_can_balance)(map)) { + return CUBEB_ERROR; + } + + WRAP(pa_cvolume_set_balance)(&vol, map, panning); + + return CUBEB_OK; } typedef struct { char * default_sink_name; char * default_source_name; - cubeb_device_info * devinfo; + cubeb_device_info ** devinfo; uint32_t max; uint32_t count; cubeb * context; @@ -1226,7 +1053,7 @@ pulse_ensure_dev_list_data_list_size (pulse_dev_list_data * list_data) if (list_data->count == list_data->max) { list_data->max += 8; list_data->devinfo = realloc(list_data->devinfo, - sizeof(cubeb_device_info) * list_data->max); + sizeof(cubeb_device_info *) * list_data->max); } } @@ -1235,47 +1062,33 @@ pulse_get_state_from_sink_port(pa_sink_port_info * info) { if (info != NULL) { #if PA_CHECK_VERSION(2, 0, 0) - if (has_pulse_v2 && info->available == PA_PORT_AVAILABLE_NO) + if (info->available == PA_PORT_AVAILABLE_NO) return CUBEB_DEVICE_STATE_UNPLUGGED; else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ #endif return CUBEB_DEVICE_STATE_ENABLED; } - return CUBEB_DEVICE_STATE_ENABLED; + return CUBEB_DEVICE_STATE_DISABLED; } static void pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, - int eol, void * user_data) + int eol, void * user_data) { pulse_dev_list_data * list_data = user_data; cubeb_device_info * devinfo; - char const * prop = NULL; - char const * device_id = NULL; + const char * prop; (void)context; - if (eol) { - WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); - return; - } - - if (info == NULL) + if (eol || info == NULL) return; - device_id = info->name; - if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) { - assert(NULL); - return; - } - - pulse_ensure_dev_list_data_list_size(list_data); - devinfo = &list_data->devinfo[list_data->count]; - memset(devinfo, 0, sizeof(cubeb_device_info)); + devinfo = calloc(1, sizeof(cubeb_device_info)); - devinfo->device_id = device_id; - devinfo->devid = (cubeb_devid) devinfo->device_id; + devinfo->device_id = strdup(info->name); + devinfo->devid = devinfo->device_id; devinfo->friendly_name = strdup(info->description); prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); if (prop) @@ -1286,8 +1099,7 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT; devinfo->state = pulse_get_state_from_sink_port(info->active_port); - devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) ? - CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + devinfo->preferred = strcmp(info->name, list_data->default_sink_name) == 0; devinfo->format = CUBEB_DEVICE_FMT_ALL; devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format); @@ -1299,7 +1111,10 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, devinfo->latency_lo = 0; devinfo->latency_hi = 0; - list_data->count += 1; + pulse_ensure_dev_list_data_list_size (list_data); + list_data->devinfo[list_data->count++] = devinfo; + + WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); } static cubeb_device_state @@ -1307,14 +1122,14 @@ pulse_get_state_from_source_port(pa_source_port_info * info) { if (info != NULL) { #if PA_CHECK_VERSION(2, 0, 0) - if (has_pulse_v2 && info->available == PA_PORT_AVAILABLE_NO) + if (info->available == PA_PORT_AVAILABLE_NO) return CUBEB_DEVICE_STATE_UNPLUGGED; else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ #endif return CUBEB_DEVICE_STATE_ENABLED; } - return CUBEB_DEVICE_STATE_ENABLED; + return CUBEB_DEVICE_STATE_DISABLED; } static void @@ -1323,28 +1138,17 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info, { pulse_dev_list_data * list_data = user_data; cubeb_device_info * devinfo; - char const * prop = NULL; - char const * device_id = NULL; + const char * prop; (void)context; - if (eol) { - WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); - return; - } - - device_id = info->name; - if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) { - assert(NULL); + if (eol) return; - } - pulse_ensure_dev_list_data_list_size(list_data); - devinfo = &list_data->devinfo[list_data->count]; - memset(devinfo, 0, sizeof(cubeb_device_info)); + devinfo = calloc(1, sizeof(cubeb_device_info)); - devinfo->device_id = device_id; - devinfo->devid = (cubeb_devid) devinfo->device_id; + devinfo->device_id = strdup(info->name); + devinfo->devid = devinfo->device_id; devinfo->friendly_name = strdup(info->description); prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); if (prop) @@ -1355,8 +1159,7 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info, devinfo->type = CUBEB_DEVICE_TYPE_INPUT; devinfo->state = pulse_get_state_from_source_port(info->active_port); - devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) ? - CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + devinfo->preferred = strcmp(info->name, list_data->default_source_name) == 0; devinfo->format = CUBEB_DEVICE_FMT_ALL; devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format); @@ -1368,7 +1171,10 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info, devinfo->latency_lo = 0; devinfo->latency_hi = 0; - list_data->count += 1; + pulse_ensure_dev_list_data_list_size (list_data); + list_data->devinfo[list_data->count++] = devinfo; + + WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); } static void @@ -1380,20 +1186,19 @@ pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata) free(list_data->default_sink_name); free(list_data->default_source_name); - list_data->default_sink_name = - i->default_sink_name ? strdup(i->default_sink_name) : NULL; - list_data->default_source_name = - i->default_source_name ? strdup(i->default_source_name) : NULL; + list_data->default_sink_name = strdup(i->default_sink_name); + list_data->default_source_name = strdup(i->default_source_name); WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); } static int pulse_enumerate_devices(cubeb * context, cubeb_device_type type, - cubeb_device_collection * collection) + cubeb_device_collection ** collection) { pulse_dev_list_data user_data = { NULL, NULL, NULL, 0, 0, context }; pa_operation * o; + uint32_t i; WRAP(pa_threaded_mainloop_lock)(context->mainloop); @@ -1424,26 +1229,15 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type, WRAP(pa_threaded_mainloop_unlock)(context->mainloop); - collection->device = user_data.devinfo; - collection->count = user_data.count; + *collection = malloc(sizeof(cubeb_device_collection) + + sizeof(cubeb_device_info *) * (user_data.count > 0 ? user_data.count - 1 : 0)); + (*collection)->count = user_data.count; + for (i = 0; i < user_data.count; i++) + (*collection)->device[i] = user_data.devinfo[i]; free(user_data.default_sink_name); free(user_data.default_source_name); - return CUBEB_OK; -} - -static int -pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection) -{ - size_t n; - - for (n = 0; n < collection->count; n++) { - free((void *) collection->device[n].friendly_name); - free((void *) collection->device[n].vendor_name); - free((void *) collection->device[n].group_id); - } - - free(collection->device); + free(user_data.devinfo); return CUBEB_OK; } @@ -1491,40 +1285,29 @@ pulse_subscribe_callback(pa_context * ctx, cubeb * context = userdata; switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { - case PA_SUBSCRIPTION_EVENT_SERVER: - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { - LOG("Server changed %d", index); - WRAP(pa_context_get_server_info)(context->context, server_info_callback, context); - } - break; case PA_SUBSCRIPTION_EVENT_SOURCE: case PA_SUBSCRIPTION_EVENT_SINK: - if (g_cubeb_log_level) { + if (g_log_level) { if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE && (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - LOG("Removing source index %d", index); + LOG("Removing sink index %d", index); } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE && (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - LOG("Adding source index %d", index); + LOG("Adding sink index %d", index); } if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK && (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { - LOG("Removing sink index %d", index); + LOG("Removing source index %d", index); } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK && (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - LOG("Adding sink index %d", index); + LOG("Adding source index %d", index); } } if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE || (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { - context->input_collection_changed_callback(context, context->input_collection_changed_user_ptr); - } - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { - context->output_collection_changed_callback(context, context->output_collection_changed_user_ptr); - } + context->collection_changed_callback(context, context->collection_changed_user_ptr); } break; } @@ -1540,15 +1323,34 @@ subscribe_success(pa_context *c, int success, void *userdata) } static int -pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) { +pulse_register_device_collection_changed(cubeb * context, + cubeb_device_type devtype, + cubeb_device_collection_changed_callback collection_changed_callback, + void * user_ptr) +{ + context->collection_changed_callback = collection_changed_callback; + context->collection_changed_user_ptr = user_ptr; + WRAP(pa_threaded_mainloop_lock)(context->mainloop); - WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context); + pa_subscription_mask_t mask; + if (context->collection_changed_callback == NULL) { + // Unregister subscription + WRAP(pa_context_set_subscribe_callback)(context->context, NULL, NULL); + mask = PA_SUBSCRIPTION_MASK_NULL; + } else { + WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context); + if (devtype == CUBEB_DEVICE_TYPE_INPUT) + mask = PA_SUBSCRIPTION_MASK_SOURCE; + else if (devtype == CUBEB_DEVICE_TYPE_OUTPUT) + mask = PA_SUBSCRIPTION_MASK_SINK; + else + mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; + } pa_operation * o; o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context); if (o == NULL) { - WRAP(pa_threaded_mainloop_unlock)(context->mainloop); LOG("Context subscribe failed"); return CUBEB_ERROR; } @@ -1560,37 +1362,6 @@ pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) { return CUBEB_OK; } -static int -pulse_register_device_collection_changed(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback collection_changed_callback, - void * user_ptr) -{ - if (devtype & CUBEB_DEVICE_TYPE_INPUT) { - context->input_collection_changed_callback = collection_changed_callback; - context->input_collection_changed_user_ptr = user_ptr; - } - if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) { - context->output_collection_changed_callback = collection_changed_callback; - context->output_collection_changed_user_ptr = user_ptr; - } - - pa_subscription_mask_t mask = PA_SUBSCRIPTION_MASK_NULL; - if (context->input_collection_changed_callback) { - /* Input added or removed */ - mask |= PA_SUBSCRIPTION_MASK_SOURCE; - } - if (context->output_collection_changed_callback) { - /* Output added or removed */ - mask |= PA_SUBSCRIPTION_MASK_SINK; - } - /* Default device changed, this is always registered in order to update the - * `default_sink_info` when the default device changes. */ - mask |= PA_SUBSCRIPTION_MASK_SERVER; - - return pulse_subscribe_notifications(context, mask); -} - static struct cubeb_ops const pulse_ops = { .init = pulse_init, .get_backend_id = pulse_get_backend_id, @@ -1598,16 +1369,15 @@ static struct cubeb_ops const pulse_ops = { .get_min_latency = pulse_get_min_latency, .get_preferred_sample_rate = pulse_get_preferred_sample_rate, .enumerate_devices = pulse_enumerate_devices, - .device_collection_destroy = pulse_device_collection_destroy, .destroy = pulse_destroy, .stream_init = pulse_stream_init, .stream_destroy = pulse_stream_destroy, .stream_start = pulse_stream_start, .stream_stop = pulse_stream_stop, - .stream_reset_default_device = NULL, .stream_get_position = pulse_stream_get_position, .stream_get_latency = pulse_stream_get_latency, .stream_set_volume = pulse_stream_set_volume, + .stream_set_panning = pulse_stream_set_panning, .stream_get_current_device = pulse_stream_get_current_device, .stream_device_destroy = pulse_stream_device_destroy, .stream_register_device_changed_callback = NULL, |