diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 8509d8ac4..544399e99 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -904,8 +904,6 @@ static int build_pollfd(struct userdata *u) { /* Called from IO context */ static int suspend(struct userdata *u) { - const char *mod_name; - pa_assert(u); pa_assert(u->pcm_handle); @@ -916,13 +914,6 @@ static int suspend(struct userdata *u) { snd_pcm_close(u->pcm_handle); u->pcm_handle = NULL; - if ((mod_name = pa_proplist_gets(u->sink->proplist, PA_ALSA_PROP_UCM_MODIFIER))) { - pa_log_info("Disable ucm modifier %s", mod_name); - - if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_dismod", mod_name) < 0) - pa_log("Failed to disable ucm modifier %s", mod_name); - } - if (u->alsa_rtpoll_item) { pa_rtpoll_item_free(u->alsa_rtpoll_item); u->alsa_rtpoll_item = NULL; @@ -1045,20 +1036,12 @@ static int unsuspend(struct userdata *u) { pa_bool_t b, d; snd_pcm_uframes_t period_size, buffer_size; char *device_name = NULL; - const char *mod_name; pa_assert(u); pa_assert(!u->pcm_handle); pa_log_info("Trying resume..."); - if ((mod_name = pa_proplist_gets(u->sink->proplist, PA_ALSA_PROP_UCM_MODIFIER))) { - pa_log_info("Enable ucm modifier %s", mod_name); - - if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0) - pa_log("Failed to enable ucm modifier %s", mod_name); - } - if ((is_iec958(u) || is_hdmi(u)) && pa_sink_is_passthrough(u->sink)) { /* Need to open device in NONAUDIO mode */ int len = strlen(u->device_name) + 8; diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 295789277..94d4a09f6 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -836,8 +836,6 @@ static int build_pollfd(struct userdata *u) { /* Called from IO context */ static int suspend(struct userdata *u) { - const char *mod_name; - pa_assert(u); pa_assert(u->pcm_handle); @@ -847,13 +845,6 @@ static int suspend(struct userdata *u) { snd_pcm_close(u->pcm_handle); u->pcm_handle = NULL; - if ((mod_name = pa_proplist_gets(u->source->proplist, PA_ALSA_PROP_UCM_MODIFIER))) { - pa_log_info("Disable ucm modifier %s", mod_name); - - if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_dismod", mod_name) < 0) - pa_log("Failed to disable ucm modifier %s", mod_name); - } - if (u->alsa_rtpoll_item) { pa_rtpoll_item_free(u->alsa_rtpoll_item); u->alsa_rtpoll_item = NULL; @@ -958,20 +949,12 @@ static int unsuspend(struct userdata *u) { int err; pa_bool_t b, d; snd_pcm_uframes_t period_size, buffer_size; - const char *mod_name; pa_assert(u); pa_assert(!u->pcm_handle); pa_log_info("Trying resume..."); - if ((mod_name = pa_proplist_gets(u->source->proplist, PA_ALSA_PROP_UCM_MODIFIER))) { - pa_log_info("Enable ucm modifier %s", mod_name); - - if (snd_use_case_set(u->ucm_context->ucm->ucm_mgr, "_enamod", mod_name) < 0) - pa_log("Failed to enable ucm modifier %s", mod_name); - } - if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK| SND_PCM_NO_AUTO_RESAMPLE| diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c index 87ec29348..81f1c2bc7 100644 --- a/src/modules/alsa/alsa-ucm.c +++ b/src/modules/alsa/alsa-ucm.c @@ -1561,3 +1561,52 @@ void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context) { pa_idxset_free(context->ucm_modifiers, NULL, NULL); } } + +/* Enable the modifier when the first stream with matched role starts */ +void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) { + pa_alsa_ucm_modifier *mod; + + if (!ucm->active_verb) + return; + + PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) { + if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) { + if (mod->enabled_counter == 0) { + const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); + + pa_log_info("Enable ucm modifier %s", mod_name); + if (snd_use_case_set(ucm->ucm_mgr, "_enamod", mod_name) < 0) { + pa_log("Failed to enable ucm modifier %s", mod_name); + } + } + + mod->enabled_counter++; + break; + } + } +} + +/* Disable the modifier when the last stream with matched role ends */ +void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir) { + pa_alsa_ucm_modifier *mod; + + if (!ucm->active_verb) + return; + + PA_LLIST_FOREACH(mod, ucm->active_verb->modifiers) { + if ((mod->action_direction == dir) && (pa_streq(mod->media_role, role))) { + + mod->enabled_counter--; + if (mod->enabled_counter == 0) { + const char *mod_name = pa_proplist_gets(mod->proplist, PA_ALSA_PROP_UCM_NAME); + + pa_log_info("Disable ucm modifier %s", mod_name); + if (snd_use_case_set(ucm->ucm_mgr, "_dismod", mod_name) < 0) { + pa_log("Failed to disable ucm modifier %s", mod_name); + } + } + + break; + } + } +} diff --git a/src/modules/alsa/alsa-ucm.h b/src/modules/alsa/alsa-ucm.h index 4c5167f1c..cdeb469e6 100644 --- a/src/modules/alsa/alsa-ucm.h +++ b/src/modules/alsa/alsa-ucm.h @@ -106,6 +106,9 @@ int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *p void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm); void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context); +void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir); +void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir); + /* UCM - Use Case Manager is available on some audio cards */ struct pa_alsa_ucm_device { @@ -147,6 +150,9 @@ struct pa_alsa_ucm_modifier { /* Non-NULL if the modifier has its own PlaybackPCM/CapturePCM */ pa_alsa_mapping *playback_mapping; pa_alsa_mapping *capture_mapping; + + /* Count how many role matched streams are running */ + int enabled_counter; }; struct pa_alsa_ucm_verb { diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index d9c059669..366f4ba46 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -124,6 +124,13 @@ struct userdata { /* ucm stuffs */ pa_bool_t use_ucm; pa_alsa_ucm_config ucm; + + /* hooks for modifier action */ + pa_hook_slot + *sink_input_put_hook_slot, + *source_output_put_hook_slot, + *sink_input_unlink_hook_slot, + *source_output_unlink_hook_slot; }; struct profile_data { @@ -459,6 +466,66 @@ static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *de pa_xfree(t); } +static pa_hook_result_t sink_input_put_hook_callback(pa_core *c, pa_sink_input *sink_input, struct userdata *u) { + const char *role; + pa_sink *sink = sink_input->sink; + + pa_assert(sink); + + role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE); + + /* new sink input linked to sink of this card */ + if (role && sink->card == u->card) + pa_alsa_ucm_roled_stream_begin(&u->ucm, role, PA_DIRECTION_OUTPUT); + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_put_hook_callback(pa_core *c, pa_source_output *source_output, struct userdata *u) { + const char *role; + pa_source *source = source_output->source; + + pa_assert(source); + + role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE); + + /* new source output linked to source of this card */ + if (role && source->card == u->card) + pa_alsa_ucm_roled_stream_begin(&u->ucm, role, PA_DIRECTION_INPUT); + + return PA_HOOK_OK; +} + +static pa_hook_result_t sink_input_unlink_hook_callback(pa_core *c, pa_sink_input *sink_input, struct userdata *u) { + const char *role; + pa_sink *sink = sink_input->sink; + + pa_assert(sink); + + role = pa_proplist_gets(sink_input->proplist, PA_PROP_MEDIA_ROLE); + + /* new sink input unlinked from sink of this card */ + if (role && sink->card == u->card) + pa_alsa_ucm_roled_stream_end(&u->ucm, role, PA_DIRECTION_OUTPUT); + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_output_unlink_hook_callback(pa_core *c, pa_source_output *source_output, struct userdata *u) { + const char *role; + pa_source *source = source_output->source; + + pa_assert(source); + + role = pa_proplist_gets(source_output->proplist, PA_PROP_MEDIA_ROLE); + + /* new source output unlinked from source of this card */ + if (role && source->card == u->card) + pa_alsa_ucm_roled_stream_end(&u->ucm, role, PA_DIRECTION_INPUT); + + return PA_HOOK_OK; +} + int pa__init(pa_module *m) { pa_card_new_data data; pa_modargs *ma; @@ -515,6 +582,20 @@ int pa__init(pa_module *m) { pa_log_info("Found UCM profiles"); u->profile_set = pa_alsa_ucm_add_profile_set(&u->ucm, &u->core->default_channel_map); + + /* hook start of sink input/source output to enable modifiers */ + /* A little bit later than module-role-cork */ + u->sink_input_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE+10, + (pa_hook_cb_t) sink_input_put_hook_callback, u); + u->source_output_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], PA_HOOK_LATE+10, + (pa_hook_cb_t) source_output_put_hook_callback, u); + + /* hook end of sink input/source output to disable modifiers */ + /* A little bit later than module-role-cork */ + u->sink_input_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE+10, + (pa_hook_cb_t) sink_input_unlink_hook_callback, u); + u->source_output_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], PA_HOOK_LATE+10, + (pa_hook_cb_t) source_output_unlink_hook_callback, u); } else { u->use_ucm = FALSE; @@ -646,6 +727,18 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) goto finish; + if (u->sink_input_put_hook_slot) + pa_hook_slot_free(u->sink_input_put_hook_slot); + + if (u->sink_input_unlink_hook_slot) + pa_hook_slot_free(u->sink_input_unlink_hook_slot); + + if (u->source_output_put_hook_slot) + pa_hook_slot_free(u->source_output_put_hook_slot); + + if (u->source_output_unlink_hook_slot) + pa_hook_slot_free(u->source_output_unlink_hook_slot); + if (u->mixer_fdl) pa_alsa_fdlist_free(u->mixer_fdl); if (u->mixer_handle)