[pulseaudio-discuss] [PATCH] profile-switcher: New module to switch card profiles based on roles

Tanu Kaskinen tanuk at iki.fi
Mon Jun 4 22:56:57 PDT 2012


On Thu, 2012-02-09 at 18:04 +0530, Arun Raghavan wrote:
> +static pa_hook_result_t sink_input_put_cb(pa_core *c, pa_sink_input *i, void *userdata) {
> +    pa_card *card;
> +    uint32_t idx1, idx2, idx3;
> +
> +    pa_assert(c);
> +    pa_assert(i);
> +
> +    /* Incoming stream -- try to switch connected Bluetooth headsets to HSP/HFP
> +     * if this is a "phone" stream, otherwise to A2DP. */
> +
> +    PA_IDXSET_FOREACH(card, c->cards, idx1) {
> +        pa_sink *sink;
> +        pa_bool_t use_a2dp = TRUE;
> +
> +        if (!card_is_bluetooth(card))
> +            continue;
> +
> +        PA_IDXSET_FOREACH(sink, card->sinks, idx2) {
> +            pa_sink_input *input;
> +
> +            PA_IDXSET_FOREACH(input, sink->inputs, idx3) {
> +                if (pa_streq(GET_ROLE(input), "phone")) {
> +                    /* There's a phone stream already attached, don't
> +                     * switch to A2DP */
> +                    use_a2dp = FALSE;
> +                }
> +            }
> +        }
> +
> +        /* Make sure we don't have an active source on this card */
> +        if (card_has_active_source(i->sink->card))
> +            use_a2dp = FALSE;
> +
> +        if (pa_streq(GET_ROLE(i), "phone")) {
> +            pa_log_info("Got a phone stream, switching card '%s' to HSP/HFP profile", card->name);
> +            pa_card_set_profile(card, "hsp", FALSE);
> +        } else if (use_a2dp) {
> +            pa_log_info("Got a non-phone stream, switching card '%s' to A2DP profile", card->name);
> +            pa_card_set_profile(card, "a2dp", FALSE);
> +        }

I think you should check what the current profile is before changing it.
The log messages are confusing if they are printed when the profile is
already what it should be.

> +    }
> +
> +    return PA_HOOK_OK;
> +}
> +
> +static void process_unlink(pa_core *c, pa_sink *sink) {
> +    pa_sink_input *input;
> +    pa_bool_t use_a2dp = TRUE;
> +    uint32_t idx;
> +
> +    PA_IDXSET_FOREACH(input, sink->inputs, idx) {
> +        if (pa_streq(GET_ROLE(input), "phone")) {
> +            /* There's a phone stream attached, don't switch */
> +            use_a2dp = FALSE;
> +            break;
> +        }
> +    }
> +
> +    /* Make sure we don't have an active source on this card */
> +    if (card_has_active_source(sink->card))
> +        use_a2dp = FALSE;
> +
> +    if (use_a2dp) {
> +        pa_log_info("Lost all phone streams, switching card '%s' to A2DP profile", sink->card->name);
> +        pa_card_set_profile(sink->card, "a2dp", FALSE);
> +    }

Maybe check here too that the current profile is not a2dp before
changing it. It's not very likely that the profile would be a2dp, but
it's probably not impossible either (if the user or something else has
changed it to a2dp during the call).

> +}
> +
> +static pa_hook_result_t sink_input_unlink_post_cb(pa_core *c, pa_sink_input *i, void *userdata) {
> +    pa_assert(c);
> +    pa_assert(i);
> +
> +    /* We only need to make changes when a "phone" stream goes away */
> +    if (!pa_streq(GET_ROLE(i), "phone") || !i->sink)
> +        return PA_HOOK_OK;
> +
> +    if (!card_is_bluetooth(i->sink->card))
> +        return PA_HOOK_OK;
> +
> +    process_unlink(c, i->sink);
> +
> +    return PA_HOOK_OK;
> +}
> +
> +/* We need this one in case a "phone" sink-input is detached while there's
> + * still a source-output connected to the device  (since the latter would block
> + * a switch to A2DP in sink_input_unlink_post_cb()) */
> +static pa_hook_result_t source_output_unlink_post_cb(pa_core *c, pa_source_output *o, void *userdata) {
> +    pa_sink *sink;
> +    uint32_t idx;
> +
> +    pa_assert(c);
> +    pa_assert(o);
> +
> +    if (!o->source || !o->source->card)
> +        return PA_HOOK_OK;
> +
> +    if (!card_is_bluetooth(o->source->card))
> +        return PA_HOOK_OK;
> +
> +    PA_IDXSET_FOREACH(sink, o->source->card->sinks, idx) {
> +        process_unlink(c, sink);
> +    }

Redundant braces.

> +
> +    return PA_HOOK_OK;
> +}
> +
> +int pa__init(pa_module*m) {
> +    pa_modargs *ma;
> +    struct userdata *u;
> +
> +    pa_assert(m);
> +
> +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
> +        pa_log("Failed to parse module arguments");
> +        return -1;
> +    }
> +
> +    m->userdata = u = pa_xnew(struct userdata, 1);
> +
> +    /* Set these all to EARLY so we can trigger profile changes asap */
> +    u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_put_cb, u);

Why don't you connect to source output put events too? Connecting only
to sink inputs is making an assumption that if there's a phone source
output, then there will also be a phone sink input. That's usually the
case, but there's no guarantee.

-- 
Tanu



More information about the pulseaudio-discuss mailing list