int pa_classify_sink(struct userdata *u, struct pa_sink *sink,
                     uint32_t flag_mask, uint32_t flag_value,
                     char *buf, int len)
{
    struct pa_classify *classify;
    struct pa_classify_device_def *defs;
    const char *name;

    pa_assert(u);
    pa_assert_se((classify = u->classify));
    pa_assert(classify->sinks);
    pa_assert_se((defs = classify->sinks->defs));

    name = pa_sink_ext_get_name(sink);

    return devices_classify(defs, sink->proplist, name,
                            flag_mask, flag_value, buf, len);
}
int pa_classify_is_port_sink_typeof(struct userdata *u, struct pa_sink *sink,
                                    const char *type,
                                    struct pa_classify_device_data **d)
{
    struct pa_classify *classify;
    struct pa_classify_device_def *defs;
    const char *name;

    pa_assert(u);
    pa_assert_se((classify = u->classify));
    pa_assert(classify->sinks);
    pa_assert_se((defs = classify->sinks->defs));

    if (!sink || !type)
        return FALSE;

    name = pa_sink_ext_get_name(sink);

    return port_device_is_typeof(defs, name, type, d);
}
static const char *object_name(struct pa_policy_object *object)
{
    const char *name;

    switch (object->type) {

    case pa_policy_object_module:
        name = pa_module_ext_get_name((struct pa_module *)object->ptr);
        break;

    case pa_policy_object_card:
        name = pa_card_ext_get_name((struct pa_card *)object->ptr);
        break;

    case pa_policy_object_sink:
        name = pa_sink_ext_get_name((struct pa_sink *)object->ptr);
        break;
        
    case pa_policy_object_source:
        name = pa_source_ext_get_name((struct pa_source *)object->ptr);
        break;
        
    case pa_policy_object_sink_input:
        name = pa_sink_input_ext_get_name((struct pa_sink_input *)object->ptr);
        break;
        
    case pa_policy_object_source_output:
        name = pa_source_output_ext_get_name(
                     (struct pa_source_output *)object->ptr);
        break;
        
    default:
        name = "<unknown>";
        break;
    }

    return name;
}
static pa_hook_result_t sink_input_neew(void *hook_data, void *call_data,
                                       void *slot_data)
{
    static uint32_t         route_flags = PA_POLICY_GROUP_FLAG_SET_SINK |
                                          PA_POLICY_GROUP_FLAG_ROUTE_AUDIO;
    static pa_volume_t      max_volume  = PA_VOLUME_NORM;


    struct pa_sink_input_new_data
                           *data = (struct pa_sink_input_new_data *)call_data;
    struct userdata        *u    = (struct userdata *)slot_data;
    uint32_t                flags;
    const char             *group_name;
    const char             *sinp_name;
    const char             *sink_name;
    int                     local_route;
    int                     local_volume;
    struct pa_policy_group *group;

    pa_assert(u);
    pa_assert(data);

    if ((group_name = pa_classify_sink_input_by_data(u,data,&flags)) != NULL &&
        (group      = pa_policy_group_find(u, group_name)          ) != NULL ){

        /* Let's just set the policy group property here already so that we
         * don't have to classify again when the sink input is put, because we
         * can just retrieve the group from the proplist. Also, this prevents
         * the classification from breaking later because of the proplist
         * overwriting done below. */
        pa_proplist_sets(data->proplist, PA_PROP_POLICY_GROUP, group_name);

        /* Proplist overwriting can also mess up the retrieval of
         * stream-specific flags later on, so we need to store those to the
         * proplist as well (ugly hack). We could probably cope without this
         * one though, since the stream-specific flags don't really seem to be
         * used. */
        pa_proplist_set(data->proplist, PA_PROP_POLICY_STREAM_FLAGS,
                        (void*)&flags, sizeof(flags));

        if (group->properties != NULL) {
            pa_proplist_update(data->proplist, PA_UPDATE_REPLACE, group->properties);
            pa_log_debug("new sink input inserted into %s. "
                         "force the following properties:", group_name);
        }

        if (group->sink != NULL) {
            sinp_name = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_NAME);

            if (!sinp_name)
                sinp_name = "<unknown>";

            local_route  = flags & PA_POLICY_LOCAL_ROUTE;
            local_volume = flags & PA_POLICY_LOCAL_VOLMAX;

            if (group->mutebyrt && !local_route) {
                sink_name = u->nullsink->name;

                pa_log_debug("force stream '%s'/'%s' to sink '%s' due to "
                             "mute-by-route", group_name,sinp_name, sink_name);

#ifdef HAVE_OLD_LIBPULSE
                data->sink = u->nullsink->sink;
#else
                pa_sink_input_new_data_set_sink(data, u->nullsink->sink, false);
#endif
            }
            else if (group->flags & route_flags) {
                sink_name = pa_sink_ext_get_name(group->sink);

                pa_log_debug("force stream '%s'/'%s' to sink '%s'",
                             group_name, sinp_name, sink_name); 

#ifdef HAVE_OLD_LIBPULSE
                data->sink = group->sink;
#else
                pa_sink_input_new_data_set_sink(data, group->sink, false);
#endif
            }

            if (local_volume) {
                pa_log_debug("force stream '%s'/'%s' volume to %d",
                             group_name, sinp_name,
                             (max_volume * 100) / PA_VOLUME_NORM);
                
                pa_cvolume_set(&data->volume, data->channel_map.channels,
                               max_volume);

                data->volume_is_set      = TRUE;
                data->save_volume        = FALSE;
            }
        }

    }


    return PA_HOOK_OK;
}