Example #1
0
static int sink_process_msg(
        pa_msgobject *o,
        int code,
        void *data,
        int64_t offset,
        pa_memchunk *chunk) {

    struct userdata *u = PA_SINK(o)->userdata;

    switch (code) {
        case PA_SINK_MESSAGE_SET_STATE:

            if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
                if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING || PA_PTR_TO_UINT(data) == PA_SINK_IDLE)
                    u->timestamp = pa_rtclock_now();
            }

            break;

        case PA_SINK_MESSAGE_GET_LATENCY: {
            pa_usec_t now;

            now = pa_rtclock_now();
            *((int64_t*) data) = (int64_t)u->timestamp - (int64_t)now;

            return 0;
        }
    }

    return pa_sink_process_msg(o, code, data, offset, chunk);
}
/* Called from main thread */
static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
    struct userdata *u;
    char *output_description;
    const char *n;

    if (!dest)
        return;

    pa_sink_input_assert_ref(i);
    pa_assert_ctl_context();
    pa_assert_se(u = i->userdata);

    output_description = pa_sprintf_malloc("Loopback to %s",
                                           pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
    pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_NAME, output_description);
    pa_xfree(output_description);

    if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
        pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_ICON_NAME, n);

    if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED)
        pa_source_output_cork(u->source_output, true);
    else
        pa_source_output_cork(u->source_output, false);

    update_adjust_timer(u);
}
static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) {
    struct device_info *d;

    pa_assert(c);
    pa_object_assert_ref(o);
    pa_assert(u);

    if (!(d = pa_hashmap_get(u->device_infos, o)))
        return PA_HOOK_OK;

    if (pa_sink_isinstance(o)) {
        pa_sink *s = PA_SINK(o);
        pa_sink_state_t state = pa_sink_get_state(s);

        if (pa_sink_check_suspend(s) <= 0)
            if (PA_SINK_IS_OPENED(state))
                restart(d);

    } else if (pa_source_isinstance(o)) {
        pa_source *s = PA_SOURCE(o);
        pa_source_state_t state = pa_source_get_state(s);

        if (pa_source_check_suspend(s) <= 0)
            if (PA_SOURCE_IS_OPENED(state))
                restart(d);
    }

    return PA_HOOK_OK;
}
Example #4
0
char *pa_sink_list_to_string(pa_core *c) {
    pa_strbuf *s;
    pa_sink *sink;
    uint32_t idx = PA_IDXSET_INVALID;
    static const char* const state_table[] = {
        [PA_SINK_RUNNING] = "RUNNING",
        [PA_SINK_SUSPENDED] = "SUSPENDED",
        [PA_SINK_IDLE] = "IDLE",
        [PA_SINK_UNLINKED] = "UNLINKED"
    };
    pa_assert(c);

    s = pa_strbuf_new();

    pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));

    for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];

        pa_strbuf_printf(
            s,
            "  %c index: %u\n"
            "\tname: <%s>\n"
            "\tdriver: <%s>\n"
            "\tflags: %s%s%s%s\n"
            "\tstate: %s\n"
            "\tvolume: <%s>\n"
            "\tmute: <%i>\n"
            "\tlatency: <%0.0f usec>\n"
            "\tmonitor source: <%u>\n"
            "\tsample spec: <%s>\n"
            "\tchannel map: <%s>\n"
            "\tused by: <%u>\n"
            "\tlinked by: <%u>\n",
            c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
            sink->index,
            sink->name,
            sink->driver,
            sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
            sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
            sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
            sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
            state_table[pa_sink_get_state(sink)],
            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)),
            !!pa_sink_get_mute(sink),
            (double) pa_sink_get_latency(sink),
            sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
            pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
            pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
            pa_sink_used_by(sink),
            pa_sink_linked_by(sink));

        if (sink->module)
            pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index);
        if (sink->description)
            pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description);
    }

    return pa_strbuf_tostring_free(s);
}
Example #5
0
/* Called from main thread */
static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
    struct userdata *u;
    pa_proplist *p;
    const char *n;

    if (!dest)
        return;

    pa_sink_input_assert_ref(i);
    pa_assert_ctl_context();
    pa_assert_se(u = i->userdata);

    p = pa_proplist_new();
    pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));

    if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
        pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);

    pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p);
    pa_proplist_free(p);

    if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED)
        pa_source_output_cork(u->source_output, true);
    else
        pa_source_output_cork(u->source_output, false);

    update_adjust_timer(u);
}
Example #6
0
void pa_core_maybe_vacuum(pa_core *c) {
    pa_assert(c);

    if (pa_idxset_isempty(c->sink_inputs) && pa_idxset_isempty(c->source_outputs)) {
        pa_log_debug("Hmm, no streams around, trying to vacuum.");
    } else {
        pa_sink *si;
        pa_source *so;
        uint32_t idx;

        idx = 0;
        PA_IDXSET_FOREACH(si, c->sinks, idx)
            if (pa_sink_get_state(si) != PA_SINK_SUSPENDED)
                return;

        idx = 0;
        PA_IDXSET_FOREACH(so, c->sources, idx)
            if (pa_source_get_state(so) != PA_SOURCE_SUSPENDED)
                return;

        pa_log_info("All sinks and sources are suspended, vacuuming memory");
    }

    pa_mempool_vacuum(c->mempool);

    if (c->rw_mempool)
        pa_mempool_vacuum(c->rw_mempool);
}
/* 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);
}
/* Called from main context */
static void sink_set_volume_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_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE);
}
/* force_state can be -1  - do not force , 0 force inactive, 1 force active */
static int perform_activity_action(pa_sink *sink, struct pa_policy_activity_variable *var, int force_state) {
    struct pa_policy_context_rule     *rule;
    union pa_policy_context_action    *actn;
    int                                is_opened;

    pa_assert(sink);

    if ((force_state != -1 && force_state == 1) ||
        (force_state == -1 && PA_SINK_IS_OPENED(pa_sink_get_state(sink)))) {
        rule = var->active_rules;
        is_opened = 1;
    } else {
        rule = var->inactive_rules;
        is_opened = 0;
    }

    for ( ;  rule != NULL;  rule = rule->next) {
        if (rule->match.method(sink->name, &rule->match.arg)) {

            if (force_state == -1 && var->sink_opened != -1 && var->sink_opened == is_opened) {
                pa_log_debug("Already executed actions for state change, skip.");
                return 1;
            }

            var->sink_opened = is_opened;

            for (actn = rule->actions; actn; actn = actn->any.next)
            {
                if (!perform_action(var->userdata, actn, NULL))
                    pa_log("Failed to perform activity action.");
            }
        }
    }

    return 1;
}
int pa__init(pa_module *m) {
    pa_modargs *ma = NULL;
    struct userdata *u;
    pa_sink *sink = NULL;
    pa_sink_input_new_data sink_input_data;
    pa_bool_t sink_dont_move;
    pa_source *source = NULL;
    pa_source_output_new_data source_output_data;
    pa_bool_t source_dont_move;
    uint32_t latency_msec;
    pa_sample_spec ss;
    pa_channel_map map;
    bool format_set = false;
    bool rate_set = false;
    bool channels_set = false;
    pa_memchunk silence;
    uint32_t adjust_time_sec;
    const char *n;
    pa_bool_t remix = TRUE;

    pa_assert(m);

    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
        pa_log("Failed to parse module arguments");
        goto fail;
    }

    n = pa_modargs_get_value(ma, "source", NULL);
    if (n && !(source = pa_namereg_get(m->core, n, PA_NAMEREG_SOURCE))) {
        pa_log("No such source.");
        goto fail;
    }

    n = pa_modargs_get_value(ma, "sink", NULL);
    if (n && !(sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK))) {
        pa_log("No such sink.");
        goto fail;
    }

    if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
        pa_log("Invalid boolean remix parameter");
        goto fail;
    }

    if (sink) {
        ss = sink->sample_spec;
        map = sink->channel_map;
        format_set = true;
        rate_set = true;
        channels_set = true;
    } else if (source) {
        ss = source->sample_spec;
        map = source->channel_map;
        format_set = true;
        rate_set = true;
        channels_set = true;
    } else {
        /* FIXME: Dummy stream format, needed because pa_sink_input_new()
         * requires valid sample spec and channel map even when all the FIX_*
         * stream flags are specified. pa_sink_input_new() should be changed
         * to ignore the sample spec and channel map when the FIX_* flags are
         * present. */
        ss.format = PA_SAMPLE_U8;
        ss.rate = 8000;
        ss.channels = 1;
        map.channels = 1;
        map.map[0] = PA_CHANNEL_POSITION_MONO;
    }

    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
        pa_log("Invalid sample format specification or channel map");
        goto fail;
    }

    if (pa_modargs_get_value(ma, "format", NULL))
        format_set = true;

    if (pa_modargs_get_value(ma, "rate", NULL))
        rate_set = true;

    if (pa_modargs_get_value(ma, "channels", NULL) || pa_modargs_get_value(ma, "channel_map", NULL))
        channels_set = true;

    latency_msec = DEFAULT_LATENCY_MSEC;
    if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 2000) {
        pa_log("Invalid latency specification");
        goto fail;
    }

    m->userdata = u = pa_xnew0(struct userdata, 1);
    u->core = m->core;
    u->module = m;
    u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC;

    adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
    if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
        pa_log("Failed to parse adjust_time value");
        goto fail;
    }

    if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC)
        u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC;
    else
        u->adjust_time = DEFAULT_ADJUST_TIME_USEC;

    pa_sink_input_new_data_init(&sink_input_data);
    sink_input_data.driver = __FILE__;
    sink_input_data.module = m;

    if (sink)
        pa_sink_input_new_data_set_sink(&sink_input_data, sink, FALSE);

    if (pa_modargs_get_proplist(ma, "sink_input_properties", sink_input_data.proplist, PA_UPDATE_REPLACE) < 0) {
        pa_log("Failed to parse the sink_input_properties value.");
        pa_sink_input_new_data_done(&sink_input_data);
        goto fail;
    }

    if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_ROLE))
        pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");

    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
    sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | PA_SINK_INPUT_START_CORKED;

    if (!remix)
        sink_input_data.flags |= PA_SINK_INPUT_NO_REMIX;

    if (!format_set)
        sink_input_data.flags |= PA_SINK_INPUT_FIX_FORMAT;

    if (!rate_set)
        sink_input_data.flags |= PA_SINK_INPUT_FIX_RATE;

    if (!channels_set)
        sink_input_data.flags |= PA_SINK_INPUT_FIX_CHANNELS;

    sink_dont_move = FALSE;
    if (pa_modargs_get_value_boolean(ma, "sink_dont_move", &sink_dont_move) < 0) {
        pa_log("sink_dont_move= expects a boolean argument.");
        goto fail;
    }

    if (sink_dont_move)
        sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE;

    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
    pa_sink_input_new_data_done(&sink_input_data);

    if (!u->sink_input)
        goto fail;

    /* If format, rate or channels were originally unset, they are set now
     * after the pa_sink_input_new() call. */
    ss = u->sink_input->sample_spec;
    map = u->sink_input->channel_map;

    u->sink_input->parent.process_msg = sink_input_process_msg_cb;
    u->sink_input->pop = sink_input_pop_cb;
    u->sink_input->process_rewind = sink_input_process_rewind_cb;
    u->sink_input->kill = sink_input_kill_cb;
    u->sink_input->state_change = sink_input_state_change_cb;
    u->sink_input->attach = sink_input_attach_cb;
    u->sink_input->detach = sink_input_detach_cb;
    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
    u->sink_input->update_max_request = sink_input_update_max_request_cb;
    u->sink_input->may_move_to = sink_input_may_move_to_cb;
    u->sink_input->moving = sink_input_moving_cb;
    u->sink_input->suspend = sink_input_suspend_cb;
    u->sink_input->userdata = u;

    pa_sink_input_set_requested_latency(u->sink_input, u->latency/3);

    pa_source_output_new_data_init(&source_output_data);
    source_output_data.driver = __FILE__;
    source_output_data.module = m;
    if (source)
        pa_source_output_new_data_set_source(&source_output_data, source, FALSE);

    if (pa_modargs_get_proplist(ma, "source_output_properties", source_output_data.proplist, PA_UPDATE_REPLACE) < 0) {
        pa_log("Failed to parse the source_output_properties value.");
        pa_source_output_new_data_done(&source_output_data);
        goto fail;
    }

    if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_ROLE))
        pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract");

    pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
    pa_source_output_new_data_set_channel_map(&source_output_data, &map);
    source_output_data.flags = PA_SOURCE_OUTPUT_START_CORKED;

    if (!remix)
        source_output_data.flags |= PA_SOURCE_OUTPUT_NO_REMIX;

    source_dont_move = FALSE;
    if (pa_modargs_get_value_boolean(ma, "source_dont_move", &source_dont_move) < 0) {
        pa_log("source_dont_move= expects a boolean argument.");
        goto fail;
    }

    if (source_dont_move)
        source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE;

    pa_source_output_new(&u->source_output, m->core, &source_output_data);
    pa_source_output_new_data_done(&source_output_data);

    if (!u->source_output)
        goto fail;

    u->source_output->parent.process_msg = source_output_process_msg_cb;
    u->source_output->push = source_output_push_cb;
    u->source_output->process_rewind = source_output_process_rewind_cb;
    u->source_output->kill = source_output_kill_cb;
    u->source_output->attach = source_output_attach_cb;
    u->source_output->detach = source_output_detach_cb;
    u->source_output->state_change = source_output_state_change_cb;
    u->source_output->may_move_to = source_output_may_move_to_cb;
    u->source_output->moving = source_output_moving_cb;
    u->source_output->suspend = source_output_suspend_cb;
    u->source_output->userdata = u;

    pa_source_output_set_requested_latency(u->source_output, u->latency/3);

    pa_sink_input_get_silence(u->sink_input, &silence);
    u->memblockq = pa_memblockq_new(
            "module-loopback memblockq",
            0,                      /* idx */
            MEMBLOCKQ_MAXLENGTH,    /* maxlength */
            MEMBLOCKQ_MAXLENGTH,    /* tlength */
            &ss,                    /* sample_spec */
            0,                      /* prebuf */
            0,                      /* minreq */
            0,                      /* maxrewind */
            &silence);              /* silence frame */
    pa_memblock_unref(silence.memblock);

    u->asyncmsgq = pa_asyncmsgq_new(0);

    if (!pa_proplist_contains(u->source_output->proplist, PA_PROP_MEDIA_NAME))
        pa_proplist_setf(u->source_output->proplist, PA_PROP_MEDIA_NAME, "Loopback to %s",
                         pa_strnull(pa_proplist_gets(u->sink_input->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));

    if (!pa_proplist_contains(u->source_output->proplist, PA_PROP_MEDIA_ICON_NAME)
            && (n = pa_proplist_gets(u->sink_input->sink->proplist, PA_PROP_DEVICE_ICON_NAME)))
        pa_proplist_sets(u->source_output->proplist, PA_PROP_MEDIA_ICON_NAME, n);

    if (!pa_proplist_contains(u->sink_input->proplist, PA_PROP_MEDIA_NAME))
        pa_proplist_setf(u->sink_input->proplist, PA_PROP_MEDIA_NAME, "Loopback from %s",
                         pa_strnull(pa_proplist_gets(u->source_output->source->proplist, PA_PROP_DEVICE_DESCRIPTION)));

    if (source && !pa_proplist_contains(u->sink_input->proplist, PA_PROP_MEDIA_ICON_NAME)
            && (n = pa_proplist_gets(u->source_output->source->proplist, PA_PROP_DEVICE_ICON_NAME)))
        pa_proplist_sets(u->sink_input->proplist, PA_PROP_MEDIA_ICON_NAME, n);

    pa_sink_input_put(u->sink_input);
    pa_source_output_put(u->source_output);

    if (pa_source_get_state(u->source_output->source) != PA_SOURCE_SUSPENDED)
	    pa_sink_input_cork(u->sink_input, FALSE);

    if (pa_sink_get_state(u->sink_input->sink) != PA_SINK_SUSPENDED)
	    pa_source_output_cork(u->source_output, FALSE);

    update_adjust_timer(u);

    pa_modargs_free(ma);
    return 0;

fail:
    if (ma)
        pa_modargs_free(ma);

    pa__done(m);

    return -1;
}
/* Called from input thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
    struct userdata *u;

    pa_source_output_assert_ref(o);
    pa_source_output_assert_io_context(o);
    pa_assert_se(u = o->userdata);

    if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) {
        pa_log("push when no link?");
        return;
    }

    /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */

    /* if uplink sink exists, pull data from there; simplify by using
       same length as chunk provided by source */
    if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) {
        pa_memchunk tchunk;
        size_t nbytes = chunk->length;
        pa_mix_info streams[2];
        pa_memchunk target_chunk;
        void *target;
        int ch;

        /* Hmm, process any rewind request that might be queued up */
        pa_sink_process_rewind(u->sink, 0);

        /* get data from the sink */
        while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) {
            pa_memchunk nchunk;

            /* make sure we get nbytes from the sink with render_full,
               otherwise we cannot mix with the uplink */
            pa_sink_render_full(u->sink, nbytes, &nchunk);
            pa_memblockq_push(u->sink_memblockq, &nchunk);
            pa_memblock_unref(nchunk.memblock);
        }
        pa_assert(tchunk.length == chunk->length);

        /* move the read pointer for sink memblockq */
        pa_memblockq_drop(u->sink_memblockq, tchunk.length);

        /* allocate target chunk */
        /* this could probably be done in-place, but having chunk as both
           the input and output creates issues with reference counts */
        target_chunk.index = 0;
        target_chunk.length = chunk->length;
        pa_assert(target_chunk.length == chunk->length);

        target_chunk.memblock = pa_memblock_new(o->source->core->mempool,
                                                target_chunk.length);
        pa_assert( target_chunk.memblock );

        /* get target pointer */
        target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock)
                         + target_chunk.index);

        /* set-up mixing structure
           volume was taken care of in sink and source already */
        streams[0].chunk = *chunk;
        for(ch=0;ch<o->sample_spec.channels;ch++)
            streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
        streams[0].volume.channels = o->sample_spec.channels;

        streams[1].chunk = tchunk;
        for(ch=0;ch<o->sample_spec.channels;ch++)
            streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */
        streams[1].volume.channels = o->sample_spec.channels;

        /* do mixing */
        pa_mix(streams,                /* 2 streams to be mixed */
               2,
               target,                 /* put result in target chunk */
               chunk->length,          /* same length as input */
               (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */
               NULL,                   /* no volume information */
               FALSE);                 /* no mute */

        pa_memblock_release(target_chunk.memblock);
        pa_memblock_unref(tchunk.memblock); /* clean-up */

        /* forward the data to the virtual source */
        pa_source_post(u->source, &target_chunk);

        pa_memblock_unref(target_chunk.memblock); /* clean-up */

    } else {
        /* forward the data to the virtual source */
        pa_source_post(u->source, chunk);
    }


}
Example #12
0
char *pa_sink_list_to_string(pa_core *c) {
    pa_strbuf *s;
    pa_sink *sink;
    uint32_t idx = PA_IDXSET_INVALID;
    pa_assert(c);

    s = pa_strbuf_new();

    pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks));

    for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX],
            cv[PA_CVOLUME_SNPRINT_MAX],
            cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
            v[PA_VOLUME_SNPRINT_MAX],
            vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
            cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
        const char *cmn;

        cmn = pa_channel_map_to_pretty_name(&sink->channel_map);


        pa_strbuf_printf(
            s,
            "  %c index: %u\n"
            "\tname: <%s>\n"
            "\tdriver: <%s>\n"
            "\tflags: %s%s%s%s%s%s%s%s\n"
            "\tstate: %s\n"
            "\tsuspend cause: %s%s%s%s\n"
            "\tpriority: %u\n"
            "\tvolume: %s%s%s\n"
            "\t        balance %0.2f\n"
            "\tbase volume: %s%s%s\n"
            "\tvolume steps: %u\n"
            "\tmuted: %s\n"
            "\tcurrent latency: %0.2f ms\n"
            "\tmax request: %lu KiB\n"
            "\tmax rewind: %lu KiB\n"
            "\tmonitor source: %u\n"
            "\tsample spec: %s\n"
            "\tchannel map: %s%s%s\n"
            "\tused by: %u\n"
            "\tlinked by: %u\n",
            sink == c->default_sink ? '*' : ' ',
            sink->index,
            sink->name,
            sink->driver,
            sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
            sink->flags & PA_SINK_NETWORK ? "NETWORK " : "",
            sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
            sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
            sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
            sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
            sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME " : "",
            sink->flags & PA_SINK_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
            sink_state_to_string(pa_sink_get_state(sink)),
            sink->suspend_cause & PA_SUSPEND_USER ? "USER " : "",
            sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",
            sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",
            sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "",
            sink->priority,
            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
            sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "",
            sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
            pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),
            pa_volume_snprint(v, sizeof(v), sink->base_volume),
            sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "",
            sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "",
            sink->n_volume_steps,
            pa_yes_no(pa_sink_get_mute(sink, FALSE)),
            (double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC,
            (unsigned long) pa_sink_get_max_request(sink) / 1024,
            (unsigned long) pa_sink_get_max_rewind(sink) / 1024,
            sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
            pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
            pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
            cmn ? "\n\t             " : "",
            cmn ? cmn : "",
            pa_sink_used_by(sink),
            pa_sink_linked_by(sink));

        if (sink->flags & PA_SINK_DYNAMIC_LATENCY) {
            pa_usec_t min_latency, max_latency;
            pa_sink_get_latency_range(sink, &min_latency, &max_latency);

            pa_strbuf_printf(
                    s,
                    "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n",
                    (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
                    (double) min_latency / PA_USEC_PER_MSEC,
                    (double) max_latency / PA_USEC_PER_MSEC);
        } else
            pa_strbuf_printf(
                    s,
                    "\tfixed latency: %0.2f ms\n",
                    (double) pa_sink_get_fixed_latency(sink) / PA_USEC_PER_MSEC);

        if (sink->card)
            pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
        if (sink->module)
            pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);

        t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t");
        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
        pa_xfree(t);

        append_port_list(s, sink->ports);

        if (sink->active_port)
            pa_strbuf_printf(
                    s,
                    "\tactive port: <%s>\n",
                    sink->active_port->name);
    }

    return pa_strbuf_tostring_free(s);
}
/* Generic sink state change logic. Used by raw_sink and voip_sink */
int voice_sink_set_state(pa_sink *s, pa_sink *other, pa_sink_state_t state) {
    struct userdata *u;
    pa_sink *om_sink;

    pa_sink_assert_ref(s);
    pa_assert_se(u = s->userdata);
    if (!other) {
        pa_log_debug("other sink not initialized or freed");
        return 0;
    }
    pa_sink_assert_ref(other);
    om_sink = u->master_sink;

    if (u->hw_sink_input && PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->hw_sink_input))) {
        if (pa_sink_input_get_state(u->hw_sink_input) == PA_SINK_INPUT_CORKED) {
            if (PA_SINK_IS_OPENED(state) ||
                PA_SINK_IS_OPENED(pa_sink_get_state(other)) ||
                pa_atomic_load(&u->cmt_connection.dl_state) == CMT_DL_ACTIVE) {
                pa_sink_input_cork(u->hw_sink_input, FALSE);
                pa_log_debug("hw_sink_input uncorked");
            }
        }
        else {
            if (state == PA_SINK_SUSPENDED &&
                pa_sink_get_state(other) == PA_SINK_SUSPENDED &&
                pa_atomic_load(&u->cmt_connection.dl_state) != CMT_DL_ACTIVE) {
                pa_sink_input_cork(u->hw_sink_input, TRUE);
                pa_log_debug("hw_sink_input corked");
            }
        }
    }

    if (om_sink == NULL) {
        pa_log_info("No master sink, assuming primary mixer tuning.\n");
        pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI);
    }
    else if (pa_atomic_load(&u->cmt_connection.dl_state) == CMT_DL_ACTIVE ||
            (pa_sink_get_state(u->voip_sink) <= PA_SINK_SUSPENDED &&
             voice_voip_sink_used_by(u))) {
        if (pa_atomic_load(&u->mixer_state) == PROP_MIXER_TUNING_PRI) {
             pa_proplist *p = pa_proplist_new();
             pa_assert(p);
             pa_proplist_sets(p, PROP_MIXER_TUNING_MODE, PROP_MIXER_TUNING_ALT_S);
             pa_sink_update_proplist(om_sink, PA_UPDATE_REPLACE, p);
             pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_ALT);
             pa_proplist_free(p);
             if (u->sidetone_enable)
                 voice_enable_sidetone(u,1);
        }
    }
    else {
        if (pa_atomic_load(&u->mixer_state) == PROP_MIXER_TUNING_ALT) {
            pa_proplist *p = pa_proplist_new();
            pa_assert(p);
            pa_proplist_sets(p, PROP_MIXER_TUNING_MODE, PROP_MIXER_TUNING_PRI_S);
            pa_sink_update_proplist(om_sink, PA_UPDATE_REPLACE, p);
            pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI);
            pa_proplist_free(p);
            voice_enable_sidetone(u,0);

        }
    }

    return 0;
}