/* Called from main context */
static void sink_set_mute_cb(pa_sink *s) {
    struct userdata *u;

    pa_sink_assert_ref(s);
    pa_assert_se(u = s->userdata);

    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
        return;

    pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
}
static void apply_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore, pa_bool_t cork) {
    pa_sink_input *j;
    uint32_t idx;

    pa_assert(u);
    pa_sink_assert_ref(s);

    for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
        pa_bool_t corked;
        const char *role;

        if (j == ignore)
            continue;

        if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
            continue;

        if (!pa_streq(role, "video") &&
            !pa_streq(role, "music"))
            continue;

        corked = !!pa_hashmap_get(u->cork_state, j);

        if (cork && !corked) {
            pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1));
            pa_sink_input_set_mute(j, TRUE, FALSE);
            pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_CORK, NULL);
        } else if (!cork) {
            pa_hashmap_remove(u->cork_state, j);

            if (corked) {
                pa_sink_input_set_mute(j, FALSE, FALSE);
                pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL);
            }
        }
    }
}
int pa_sink_input_ext_set_volume_limit(struct pa_sink_input *sinp,
                                       pa_volume_t limit)
{
    pa_sink     *sink;
    int          retval;
    uint64_t     limit64;
    pa_volume_t  value;
    pa_cvolume  *factor;
    pa_cvolume  *real;
    int          changed;
    int          i;

    pa_assert(sinp);
    pa_assert_se((sink = sinp->sink));

    retval = 0;

    if (limit == 0)
        pa_sink_input_set_mute(sinp, TRUE, TRUE);
    else {
        pa_sink_input_set_mute(sinp, FALSE, TRUE);

        if (limit > PA_VOLUME_NORM)
            limit = PA_VOLUME_NORM;

        factor  = &sinp->volume_factor;
        real    = &sinp->real_ratio;
        limit64 = (uint64_t)limit * (uint64_t)PA_VOLUME_NORM;
        changed = FALSE;

        if (real->channels != factor->channels) {
            pa_log_debug("channel number mismatch");
            retval = -1;
        }
        else {
            for (i = 0;   i < factor->channels;   i++) {
                if (limit < real->values[i])
                    value = limit64 / (uint64_t)real->values[i];
                else
                    value = PA_VOLUME_NORM;

                if (value != factor->values[i]) {
                    changed = 1;
                    factor->values[i] = value;
                }
            }

            if (changed) {
                if (pa_sink_flat_volume_enabled(sink))
                    retval = 1;
                else {
                    pa_sw_cvolume_multiply(&sinp->soft_volume, real, factor);
                    pa_asyncmsgq_send(sink->asyncmsgq, PA_MSGOBJECT(sinp),
                                      PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME,
                                      NULL, 0, NULL);
                }
            }
        }
    }

    return retval;
}