Example #1
0
static void process_render(struct context *context, pa_usec_t now)
{
	pa_memchunk chunk;
	int request_bytes;

	pa_assert(context);

	if (context->got_max_latency)
	{
		return;
	}

	pa_log_debug("process_render: u->block_usec %lu", context->block_usec);
	while (context->timestamp < now + context->block_usec)
	{
		request_bytes = context->sink->thread_info.max_request;
		request_bytes = MIN(request_bytes, 16 * 1024);
		pa_sink_render(context->sink, request_bytes, &chunk);
		pa_log("process_render: %lu bytes", chunk.length);
		data_send(context, &chunk);
		pa_memblock_unref(chunk.memblock);
		context->timestamp += pa_bytes_to_usec(chunk.length, &context->sink->sample_spec);
	}
}
static void register_object(struct pa_policy_object *object,
                            enum pa_policy_object_type type,
                            const char *name, void *ptr, int lineno)
{
    const char    *type_str;

    if (object->type == type && object->match.method(name,&object->match.arg)){

        type_str = object_type_str(type);

        if (object->ptr != NULL) {
            pa_log("multiple match for %s '%s' (line %d in config file)",
                   type_str, name, lineno);
        }
        else {
            pa_log_debug("registering context-rule for %s '%s' "
                         "(line %d in config file)", type_str, name, lineno);

            object->ptr   = ptr;
            object->index = object_index(type, ptr);

        }
    }
}
static void handle_removed_sink_input(struct userdata      *u,
                                      struct pa_sink_input *sinp)
{
    struct pa_policy_group *group = NULL;
    struct pa_sink_input_ext *ext;
    struct pa_sink *sink;
    uint32_t        idx;
    const char     *snam;
    uint32_t        flags;

    if (sinp && u) {
        idx  = sinp->index;
        sink = sinp->sink;
        snam = sink_input_ext_get_name(sinp->proplist);
        pa_assert_se((group = get_group_or_classify(u, sinp, &flags)));

        if (flags & PA_POLICY_LOCAL_ROUTE)
            pa_sink_ext_restore_port(u, sink);

        if (flags & PA_POLICY_LOCAL_MUTE)
            pa_policy_groupset_restore_volume(u, sink);
            
        pa_policy_context_unregister(u, pa_policy_object_sink_input,
                                     snam, sinp, sinp->index);
        pa_policy_group_remove_sink_input(u, sinp->index);

        if ((ext = pa_index_hash_remove(u->hsi, idx)) == NULL)
            pa_log("no extension found for sink-input '%s' (idx=%u)",snam,idx);
        else {
            pa_xfree(ext);
        }

        pa_log_debug("removed sink_input '%s' (idx=%d) (group=%s)",
                     snam, idx, group->name);
    }
}
/* 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;
}
static pa_hook_result_t process(struct userdata *u, pa_proplist *p) {
    struct rule *r;
    time_t now;
    const char *pn;

    pa_assert(u);
    pa_assert(p);

    if (!(pn = pa_proplist_gets(p, PA_PROP_APPLICATION_PROCESS_BINARY)))
        return PA_HOOK_OK;

    if (*pn == '.' || strchr(pn, '/'))
        return PA_HOOK_OK;

    time(&now);

    pa_log_debug("Looking for .desktop file for %s", pn);

    if ((r = pa_hashmap_get(u->cache, pn))) {
        if (now-r->timestamp > STAT_INTERVAL) {
            r->timestamp = now;
            update_rule(r);
        }
    } else {
        make_room(u->cache);

        r = pa_xnew0(struct rule, 1);
        r->process_name = pa_xstrdup(pn);
        r->timestamp = now;
        pa_hashmap_put(u->cache, r->process_name, r);
        update_rule(r);
    }

    apply_rule(r, p);
    return PA_HOOK_OK;
}
Example #6
0
static void sink_update_requested_latency_cb(pa_sink *s)
{
	struct context *context;
	size_t nbytes;

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

	context->block_usec = BLOCK_USEC;
	//context->block_usec = pa_sink_get_requested_latency_within_thread(s);
	pa_log("1 block_usec %lu", context->block_usec);

	context->got_max_latency = 0;
	if (context->block_usec == (pa_usec_t) -1)
	{
		context->block_usec = s->thread_info.max_latency;
		pa_log_debug("2 block_usec %lu", context->block_usec);
		context->got_max_latency = 1;
	}

	nbytes = pa_usec_to_bytes(context->block_usec, &s->sample_spec);
	pa_sink_set_max_rewind_within_thread(s, nbytes);
	pa_sink_set_max_request_within_thread(s, nbytes);
}
static void streams_add(struct pa_classify_stream_def **defs, const char *prop,
                        enum pa_classify_method method, const char *arg, const char *clnam,
                        const char *sname, uid_t uid, const char *exe, const char *group, uint32_t flags)
{
    struct pa_classify_stream_def *d;
    struct pa_classify_stream_def *prev;
    pa_proplist *proplist = NULL;
    char         method_def[256];

    pa_assert(defs);
    pa_assert(group);

    proplist = pa_proplist_new();

    if (prop && arg && (method == pa_method_equals)) {
        pa_proplist_sets(proplist, prop, arg);
    }

    if ((d = streams_find(defs, proplist, clnam, sname, uid, exe, &prev)) != NULL) {
        pa_log_info("redefinition of stream");
        pa_xfree(d->group);
    }
    else {
        d = pa_xnew0(struct pa_classify_stream_def, 1);

        snprintf(method_def, sizeof(method_def), "<no-property-check>");

        if (prop && arg && method > pa_method_min && method < pa_method_max) {
            d->prop = pa_xstrdup(prop);

            switch (method) {

            case pa_method_equals:
                snprintf(method_def, sizeof(method_def),
                         "%s equals:%s", prop, arg);
                d->method = pa_classify_method_equals;
                d->arg.string = pa_xstrdup(arg);
                break;

            case pa_method_startswith:
                snprintf(method_def, sizeof(method_def),
                         "%s startswith:%s",prop, arg);
                d->method = pa_classify_method_startswith;
                d->arg.string = pa_xstrdup(arg);
                break;

            case pa_method_matches:
                snprintf(method_def, sizeof(method_def),
                         "%s matches:%s",prop, arg);
                d->method = pa_classify_method_matches;
                if (regcomp(&d->arg.rexp, arg, 0) != 0) {
                    pa_log("%s: invalid regexp definition '%s'",
                           __FUNCTION__, arg);
                    pa_assert_se(0);
                }
                break;


            case pa_method_true:
                snprintf(method_def, sizeof(method_def), "%s true", prop);
                d->method = pa_classify_method_true;
                memset(&d->arg, 0, sizeof(d->arg));
                break;

            default:
                /* never supposed to get here. just keep the compiler happy */
                pa_assert_se(0);
                break;
            }
        }

        d->uid   = uid;
        d->exe   = exe   ? pa_xstrdup(exe)   : NULL;
        d->clnam = clnam ? pa_xstrdup(clnam) : NULL;
        d->sname = sname ? pa_xstrdup(sname) : NULL;
        d->sact  = sname ? 0 : -1;
        
        prev->next = d;

        pa_log_debug("stream added (%d|%s|%s|%s|%d)", uid, exe?exe:"<null>",
                     clnam?clnam:"<null>", method_def, d->sact);
    }

    d->group = pa_xstrdup(group);
    d->flags = flags;

    pa_proplist_free(proplist);
}
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    int read_type = 0;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    pa_thread_mq_install(&u->thread_mq);

    for (;;) {
        int ret;
        struct pollfd *pollfd;

        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

        /* Try to read some data and pass it on to the source driver */
        if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) {
            ssize_t l;
            void *p;

            if (!u->memchunk.memblock) {
                u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd));
                u->memchunk.index = u->memchunk.length = 0;
            }

            pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index);

            p = pa_memblock_acquire(u->memchunk.memblock);
            l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
            pa_memblock_release(u->memchunk.memblock);

            pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */

            if (l < 0) {

                if (errno == EINTR)
                    continue;
                else if (errno != EAGAIN) {
                    pa_log("Failed to read data from FIFO: %s", pa_cstrerror(errno));
                    goto fail;
                }

            } else {

                u->memchunk.length = (size_t) l;
                pa_source_post(u->source, &u->memchunk);
                u->memchunk.index += (size_t) l;

                if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {
                    pa_memblock_unref(u->memchunk.memblock);
                    pa_memchunk_reset(&u->memchunk);
                }

                pollfd->revents = 0;
            }
        }

        /* Hmm, nothing to do. Let's sleep */
        pollfd->events = (short) (u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0);

        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

        if (pollfd->revents & ~POLLIN) {
            pa_log("FIFO shutdown.");
            goto fail;
        }
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
static void line_callback(pa_ioline *line, const char *s, void *userdata) {
    char *delimpos;
    char *s2, *s2p;

    pa_rtsp_client *c = userdata;
    pa_assert(line);
    pa_assert(c);
    pa_assert(c->callback);

    if (!s) {
        /* Keep the ioline/iochannel open as they will be freed automatically */
        c->ioline = NULL;
        c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
        return;
    }

    s2 = pa_xstrdup(s);
    /* Trim trailing carriage returns */
    s2p = s2 + strlen(s2) - 1;
    while (s2p >= s2 && '\r' == *s2p) {
        *s2p = '\0';
        s2p -= 1;
    }
    if (c->waiting && 0 == strcmp("RTSP/1.0 200 OK", s2)) {
        c->waiting = 0;
        if (c->response_headers)
            pa_headerlist_free(c->response_headers);
        c->response_headers = pa_headerlist_new();
        goto exit;
    }
    if (c->waiting) {
        pa_log_warn("Unexpected response: %s", s2);
        goto exit;;
    }
    if (!strlen(s2)) {
        /* End of headers */
        /* We will have a header left from our looping iteration, so add it in :) */
        if (c->last_header) {
            char *tmp = pa_strbuf_tostring_free(c->header_buffer);
            /* This is not a continuation header so let's dump it into our proplist */
            pa_headerlist_puts(c->response_headers, c->last_header, tmp);
            pa_xfree(tmp);
            pa_xfree(c->last_header);
            c->last_header = NULL;
            c->header_buffer = NULL;
        }

        pa_log_debug("Full response received. Dispatching");
        headers_read(c);
        c->waiting = 1;
        goto exit;
    }

    /* Read and parse a header (we know it's not empty) */
    /* TODO: Move header reading into the headerlist. */

    /* If the first character is a space, it's a continuation header */
    if (c->last_header && ' ' == s2[0]) {
        pa_assert(c->header_buffer);

        /* Add this line to the buffer (sans the space. */
        pa_strbuf_puts(c->header_buffer, &(s2[1]));
        goto exit;
    }

    if (c->last_header) {
        char *tmp = pa_strbuf_tostring_free(c->header_buffer);
        /* This is not a continuation header so let's dump the full
          header/value into our proplist */
        pa_headerlist_puts(c->response_headers, c->last_header, tmp);
        pa_xfree(tmp);
        pa_xfree(c->last_header);
        c->last_header = NULL;
        c->header_buffer = NULL;
    }

    delimpos = strstr(s2, ":");
    if (!delimpos) {
        pa_log_warn("Unexpected response when expecting header: %s", s);
        goto exit;
    }

    pa_assert(!c->header_buffer);
    pa_assert(!c->last_header);

    c->header_buffer = pa_strbuf_new();
    if (strlen(delimpos) > 1) {
        /* Cut our line off so we can copy the header name out */
        *delimpos++ = '\0';

        /* Trim the front of any spaces */
        while (' ' == *delimpos)
            ++delimpos;

        pa_strbuf_puts(c->header_buffer, delimpos);
    } else {
        /* Cut our line off so we can copy the header name out */
        *delimpos = '\0';
    }

    /* Save the header name */
    c->last_header = pa_xstrdup(s2);
  exit:
    pa_xfree(s2);
}
Example #10
0
static int trigger(struct userdata *u, pa_bool_t quick) {
    int enable_bits = 0, zero = 0;

    pa_assert(u);

    if (u->fd < 0)
        return 0;

    pa_log_debug("trigger");

    if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
        enable_bits |= PCM_ENABLE_INPUT;

    if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
        enable_bits |= PCM_ENABLE_OUTPUT;

    pa_log_debug("trigger: %i", enable_bits);


    if (u->use_mmap) {

        if (!quick)
            ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &zero);

#ifdef SNDCTL_DSP_HALT
        if (enable_bits == 0)
            if (ioctl(u->fd, SNDCTL_DSP_HALT, NULL) < 0)
                pa_log_warn("SNDCTL_DSP_HALT: %s", pa_cstrerror(errno));
#endif

        if (ioctl(u->fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) < 0)
            pa_log_warn("SNDCTL_DSP_SETTRIGGER: %s", pa_cstrerror(errno));

        if (u->sink && !(enable_bits & PCM_ENABLE_OUTPUT)) {
            pa_log_debug("clearing playback buffer");
            pa_silence_memory(u->out_mmap, u->out_hwbuf_size, &u->sink->sample_spec);
        }

    } else {

        if (enable_bits)
            if (ioctl(u->fd, SNDCTL_DSP_POST, NULL) < 0)
                pa_log_warn("SNDCTL_DSP_POST: %s", pa_cstrerror(errno));

        if (!quick) {
            /*
             * Some crappy drivers do not start the recording until we
             * read something.  Without this snippet, poll will never
             * register the fd as ready.
             */

            if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
                uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);

                if (pa_read(u->fd, buf, u->in_fragment_size, NULL) < 0) {
                    pa_log("pa_read() failed: %s", pa_cstrerror(errno));
                    pa_xfree(buf);
                    return -1;
                }

                pa_xfree(buf);
            }
        }
    }

    return 0;
}
/* Called from output thread context */
static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
    struct userdata *u = PA_SINK_INPUT(obj)->userdata;

    switch (code) {

        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
            pa_usec_t *r = data;

            pa_sink_input_assert_io_context(u->sink_input);

            *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec);

            /* Fall through, the default handler will add in the extra
             * latency added by the resampler */
            break;
        }

        case SINK_INPUT_MESSAGE_POST:

            pa_sink_input_assert_io_context(u->sink_input);

            if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
                pa_memblockq_push_align(u->memblockq, chunk);
            else
                pa_memblockq_flush_write(u->memblockq, TRUE);

            update_min_memblockq_length(u);

            /* Is this the end of an underrun? Then let's start things
             * right-away */
            if (!u->in_pop &&
                u->sink_input->thread_info.underrun_for > 0 &&
                pa_memblockq_is_readable(u->memblockq)) {

                pa_log_debug("Requesting rewind due to end of underrun.");
                pa_sink_input_request_rewind(u->sink_input,
                                             (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for),
                                             FALSE, TRUE, FALSE);
            }

            u->recv_counter += (int64_t) chunk->length;

            return 0;

        case SINK_INPUT_MESSAGE_REWIND:

            pa_sink_input_assert_io_context(u->sink_input);

            if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
                pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, TRUE);
            else
                pa_memblockq_flush_write(u->memblockq, TRUE);

            u->recv_counter -= offset;

            update_min_memblockq_length(u);

            return 0;

        case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
            size_t length;

            update_min_memblockq_length(u);

            length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);

            u->latency_snapshot.recv_counter = u->recv_counter;
            u->latency_snapshot.sink_input_buffer =
                pa_memblockq_get_length(u->memblockq) +
                (u->sink_input->thread_info.resampler ? pa_resampler_request(u->sink_input->thread_info.resampler, length) : length);
            u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink);

            u->latency_snapshot.max_request = pa_sink_input_get_max_request(u->sink_input);

            u->latency_snapshot.min_memblockq_length = u->min_memblockq_length;
            u->min_memblockq_length = (size_t) -1;

            return 0;
        }

        case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED: {
            /* This message is sent from the IO thread to the main
             * thread! So don't be confused. All the user cases above
             * are executed in thread context, but this one is not! */

            pa_assert_ctl_context();

            if (u->time_event)
                adjust_rates(u);
            return 0;
        }
    }

    return pa_sink_input_process_msg(obj, code, data, offset, chunk);
}
Example #12
0
int pa_context_connect(
        pa_context *c,
        const char *server,
        pa_context_flags_t flags,
        const pa_spawn_api *api) {

    int r = -1;

    pa_assert(c);
    pa_assert(PA_REFCNT_VALUE(c) >= 1);

    PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED);
    PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE);
    PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID);
    PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID);

    if (server)
        c->conf->autospawn = FALSE;
    else
        server = c->conf->default_server;

    pa_context_ref(c);

    c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);
    c->server_specified = !!server;
    pa_assert(!c->server_list);

    if (server) {
        if (!(c->server_list = pa_strlist_parse(server))) {
            pa_context_fail(c, PA_ERR_INVALIDSERVER);
            goto finish;
        }

    } else {
        char *d;

        /* Prepend in reverse order */

        /* Follow the X display */
        if (c->conf->auto_connect_display) {
            if ((d = getenv("DISPLAY"))) {
                d = pa_xstrndup(d, strcspn(d, ":"));

                if (*d)
                    c->server_list = pa_strlist_prepend(c->server_list, d);

                pa_xfree(d);
            }
        }

        /* Add TCP/IP on the localhost */
        if (c->conf->auto_connect_localhost) {
            c->server_list = pa_strlist_prepend(c->server_list, "tcp6:[::1]");
            c->server_list = pa_strlist_prepend(c->server_list, "tcp4:127.0.0.1");
        }

        /* The system wide instance via PF_LOCAL */
        c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);

        /* The user instance via PF_LOCAL */
        c->server_list = prepend_per_user(c->server_list);
    }

    /* Set up autospawning */
    if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) {

#ifdef HAVE_GETUID
        if (getuid() == 0)
            pa_log_debug("Not doing autospawn since we are root.");
        else {
            c->do_autospawn = TRUE;

            if (api)
                c->spawn_api = *api;
        }
#endif
    }

    pa_context_set_state(c, PA_CONTEXT_CONNECTING);
    r = try_next_connection(c);

finish:
    pa_context_unref(c);

    return r;
}
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;
}
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;
}
Example #15
0
int main(int argc, char *argv[]) {
    pa_mempool *pool = NULL;
    pa_sample_spec a, b;
    int ret = 1, c;
    bool all_formats = true;
    pa_resample_method_t method;
    int seconds;
    unsigned crossover_freq = 120;

    static const struct option long_options[] = {
        {"help",                  0, NULL, 'h'},
        {"verbose",               0, NULL, 'v'},
        {"version",               0, NULL, ARG_VERSION},
        {"from-rate",             1, NULL, ARG_FROM_SAMPLERATE},
        {"from-format",           1, NULL, ARG_FROM_SAMPLEFORMAT},
        {"from-channels",         1, NULL, ARG_FROM_CHANNELS},
        {"to-rate",               1, NULL, ARG_TO_SAMPLERATE},
        {"to-format",             1, NULL, ARG_TO_SAMPLEFORMAT},
        {"to-channels",           1, NULL, ARG_TO_CHANNELS},
        {"seconds",               1, NULL, ARG_SECONDS},
        {"resample-method",       1, NULL, ARG_RESAMPLE_METHOD},
        {"dump-resample-methods", 0, NULL, ARG_DUMP_RESAMPLE_METHODS},
        {NULL,                    0, NULL, 0}
    };

    setlocale(LC_ALL, "");
#ifdef ENABLE_NLS
    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
#endif

    pa_log_set_level(PA_LOG_WARN);
    if (!getenv("MAKE_CHECK"))
        pa_log_set_level(PA_LOG_INFO);

    pa_assert_se(pool = pa_mempool_new(false, 0));

    a.channels = b.channels = 1;
    a.rate = b.rate = 44100;
    a.format = b.format = PA_SAMPLE_S16LE;

    method = PA_RESAMPLER_AUTO;
    seconds = 60;

    while ((c = getopt_long(argc, argv, "hv", long_options, NULL)) != -1) {

        switch (c) {
            case 'h' :
                help(argv[0]);
                ret = 0;
                goto quit;

            case 'v':
                pa_log_set_level(PA_LOG_DEBUG);
                break;

            case ARG_VERSION:
                printf(_("%s %s\n"), argv[0], PACKAGE_VERSION);
                ret = 0;
                goto quit;

            case ARG_DUMP_RESAMPLE_METHODS:
                dump_resample_methods();
                ret = 0;
                goto quit;

            case ARG_FROM_CHANNELS:
                a.channels = (uint8_t) atoi(optarg);
                break;

            case ARG_FROM_SAMPLEFORMAT:
                a.format = pa_parse_sample_format(optarg);
                all_formats = false;
                break;

            case ARG_FROM_SAMPLERATE:
                a.rate = (uint32_t) atoi(optarg);
                break;

            case ARG_TO_CHANNELS:
                b.channels = (uint8_t) atoi(optarg);
                break;

            case ARG_TO_SAMPLEFORMAT:
                b.format = pa_parse_sample_format(optarg);
                all_formats = false;
                break;

            case ARG_TO_SAMPLERATE:
                b.rate = (uint32_t) atoi(optarg);
                break;

            case ARG_SECONDS:
                seconds = atoi(optarg);
                break;

            case ARG_RESAMPLE_METHOD:
                if (*optarg == '\0' || pa_streq(optarg, "help")) {
                    dump_resample_methods();
                    ret = 0;
                    goto quit;
                }
                method = pa_parse_resample_method(optarg);
                break;

            default:
                goto quit;
        }
    }

    ret = 0;
    pa_assert_se(pool = pa_mempool_new(false, 0));

    if (!all_formats) {

        pa_resampler *resampler;
        pa_memchunk i, j;
        pa_usec_t ts;

        pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
        pa_log_debug("=== %d seconds: %d Hz %d ch (%s) -> %d Hz %d ch (%s)", seconds,
                   a.rate, a.channels, pa_sample_format_to_string(a.format),
                   b.rate, b.channels, pa_sample_format_to_string(b.format));

        ts = pa_rtclock_now();
        pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
        pa_log_info("init: %llu", (long long unsigned)(pa_rtclock_now() - ts));

        i.memblock = pa_memblock_new(pool, pa_usec_to_bytes(1*PA_USEC_PER_SEC, &a));

        ts = pa_rtclock_now();
        i.length = pa_memblock_get_length(i.memblock);
        i.index = 0;
        while (seconds--) {
            pa_resampler_run(resampler, &i, &j);
            if (j.memblock)
                pa_memblock_unref(j.memblock);
        }
        pa_log_info("resampling: %llu", (long long unsigned)(pa_rtclock_now() - ts));
        pa_memblock_unref(i.memblock);

        pa_resampler_free(resampler);

        goto quit;
    }

    for (a.format = 0; a.format < PA_SAMPLE_MAX; a.format ++) {
        for (b.format = 0; b.format < PA_SAMPLE_MAX; b.format ++) {
            pa_resampler *forth, *back;
            pa_memchunk i, j, k;

            pa_log_debug("=== %s -> %s -> %s -> /2",
                       pa_sample_format_to_string(a.format),
                       pa_sample_format_to_string(b.format),
                       pa_sample_format_to_string(a.format));

            pa_assert_se(forth = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
            pa_assert_se(back = pa_resampler_new(pool, &b, NULL, &a, NULL, crossover_freq, method, 0));

            i.memblock = generate_block(pool, &a);
            i.length = pa_memblock_get_length(i.memblock);
            i.index = 0;
            pa_resampler_run(forth, &i, &j);
            pa_resampler_run(back, &j, &k);

            dump_block("before", &a, &i);
            dump_block("after", &b, &j);
            dump_block("reverse", &a, &k);

            pa_memblock_unref(i.memblock);
            pa_memblock_unref(j.memblock);
            pa_memblock_unref(k.memblock);

            pa_resampler_free(forth);
            pa_resampler_free(back);
        }
    }

 quit:
    if (pool)
        pa_mempool_free(pool);

    return ret;
}
Example #16
0
static int context_autospawn(pa_context *c) {
    pid_t pid;
    int status, r;
    struct sigaction sa;

    pa_context_ref(c);

    if (sigaction(SIGCHLD, NULL, &sa) < 0) {
        pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
        pa_context_fail(c, PA_ERR_INTERNAL);
        goto fail;
    }

    if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
        pa_log_debug("Process disabled waitpid(), cannot autospawn.");
        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto fail;
    }

    pa_log_debug("Trying to autospawn...");

    if (c->spawn_api.prefork)
        c->spawn_api.prefork();

    if ((pid = fork()) < 0) {
        pa_log_error(_("fork(): %s"), pa_cstrerror(errno));
        pa_context_fail(c, PA_ERR_INTERNAL);

        if (c->spawn_api.postfork)
            c->spawn_api.postfork();

        goto fail;
    } else if (!pid) {
        /* Child */

        const char *state = NULL;
        const char * argv[32];
        unsigned n = 0;

        if (c->spawn_api.atfork)
            c->spawn_api.atfork();

        /* We leave most of the cleaning up of the process environment
         * to the executable. We only clean up the file descriptors to
         * make sure the executable can actually be loaded
         * correctly. */
        pa_close_all(-1);

        /* Setup argv */
        argv[n++] = c->conf->daemon_binary;
        argv[n++] = "--start";

        while (n < PA_ELEMENTSOF(argv)-1) {
            char *a;

            if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
                break;

            argv[n++] = a;
        }

        argv[n++] = NULL;
        pa_assert(n <= PA_ELEMENTSOF(argv));

        execv(argv[0], (char * const *) argv);
        _exit(1);
    }

    /* Parent */

    if (c->spawn_api.postfork)
        c->spawn_api.postfork();

    do {
        r = waitpid(pid, &status, 0);
    } while (r < 0 && errno == EINTR);

    if (r < 0) {

        if (errno != ESRCH) {
            pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
            pa_context_fail(c, PA_ERR_INTERNAL);
            goto fail;
        }

        /* hmm, something already reaped our child, so we assume
         * startup worked, even if we cannot know */

    } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto fail;
    }

    pa_context_unref(c);

    return 0;

fail:

    pa_context_unref(c);

    return -1;
}
Example #17
0
static int try_next_connection(pa_context *c) {
    char *u = NULL;
    int r = -1;

    pa_assert(c);
    pa_assert(!c->client);

    for (;;) {
        pa_xfree(u);
        u = NULL;

        c->server_list = pa_strlist_pop(c->server_list, &u);

        if (!u) {

#ifndef OS_IS_WIN32
            if (c->do_autospawn) {

                if ((r = context_autospawn(c)) < 0)
                    goto finish;

                /* Autospawn only once */
                c->do_autospawn = FALSE;

                /* Connect only to per-user sockets this time */
                c->server_list = prepend_per_user(c->server_list);

                /* Retry connection */
                continue;
            }
#endif

#ifdef HAVE_DBUS
            if (c->no_fail && !c->server_specified) {
                if (!c->session_bus)
                    track_pulseaudio_on_dbus(c, DBUS_BUS_SESSION, &c->session_bus);
                if (!c->system_bus)
                    track_pulseaudio_on_dbus(c, DBUS_BUS_SYSTEM, &c->system_bus);
            } else
#endif
                pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);

            goto finish;
        }

        pa_log_debug("Trying to connect to %s...", u);

        pa_xfree(c->server);
        c->server = pa_xstrdup(u);

        if (!(c->client = pa_socket_client_new_string(c->mainloop, c->use_rtclock, u, PA_NATIVE_DEFAULT_PORT)))
            continue;

        c->is_local = !!pa_socket_client_is_local(c->client);
        pa_socket_client_set_callback(c->client, on_connection, c);
        break;
    }

    r = 0;

finish:
    pa_xfree(u);

    return r;
}
int pa__init(pa_module *m)
{
  pa_modargs *ma;
  const char *master_sink_name;
  const char *master_source_name;
  const char *max_hw_frag_size_str;
  const char *aep_runtime;
  pa_source *master_source;
  struct userdata *u;
  pa_proplist *p;
  pa_sink *master_sink;
  const char *raw_sink_name;
  const char *raw_source_name;
  const char *voice_sink_name;
  const char *voice_source_name;
  const char *dbus_type;
  int max_hw_frag_size = 3840;

  pa_assert(m);

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

  voice_turn_sidetone_down();
  master_sink_name = pa_modargs_get_value(ma, "master_sink", NULL);
  master_source_name = pa_modargs_get_value(ma, "master_source", NULL);
  raw_sink_name = pa_modargs_get_value(ma, "raw_sink_name", "sink.voice.raw");
  raw_source_name = pa_modargs_get_value(ma, "raw_source_name",
                                         "source.voice.raw");
  voice_sink_name = pa_modargs_get_value(ma, "voice_sink_name", "sink.voice");
  voice_source_name = pa_modargs_get_value(ma, "voice_source_name",
                                           "source.voice");
  dbus_type = pa_modargs_get_value(ma, "dbus_type", "session");
  max_hw_frag_size_str = pa_modargs_get_value(ma, "max_hw_frag_size", "3840");
  aep_runtime = pa_modargs_get_value(ma, "aep_runtime",
                                     "bbaid1n-wr0-h9a22b--dbxpb--");
  voice_set_aep_runtime_switch(aep_runtime);
  pa_log_debug("Got arguments: master_sink=\"%s\" master_source=\"%s\" raw_sink_name=\"%s\" raw_source_name=\"%s\" dbus_type=\"%s\" max_hw_frag_size=\"%s\". ",
               master_sink_name, master_source_name, raw_sink_name,
               raw_source_name, dbus_type, max_hw_frag_size_str);

  if (!(master_sink = pa_namereg_get(m->core, master_sink_name, PA_NAMEREG_SINK)))
  {
    pa_log("Master sink \"%s\" not found", master_sink_name);
    goto fail;
  }

  if (!(master_source = pa_namereg_get(m->core, master_source_name, PA_NAMEREG_SOURCE)))
  {
    pa_log( "Master source \"%s\" not found", master_source_name);
    goto fail;
  }

  if (master_sink->sample_spec.format != master_source->sample_spec.format &&
      master_sink->sample_spec.rate != master_source->sample_spec.rate &&
      master_sink->sample_spec.channels != master_source->sample_spec.channels)
  {
    pa_log("Master source and sink must have same sample spec");
    goto fail;
  }

  if (pa_atoi(max_hw_frag_size_str, &max_hw_frag_size) < 0 ||
      max_hw_frag_size < 960 ||
      max_hw_frag_size > 128*960)
  {
    pa_log("Bad value for max_hw_frag_size: %s", max_hw_frag_size_str);
    goto fail;
  }

  m->userdata = u = pa_xnew0(struct userdata, 1);
  u->core = m->core;
  u->module = m;
  u->modargs = ma;
  u->master_sink = master_sink;
  u->master_source = master_source;
  u->mainloop_handler = voice_mainloop_handler_new(u);;
  u->ul_timing_advance = 500;  // = 500 micro seconds, seems to be a good default value

  pa_channel_map_init_mono(&u->mono_map);
  pa_channel_map_init_stereo(&u->stereo_map);

  u->hw_sample_spec.format = PA_SAMPLE_S16NE;
  u->hw_sample_spec.rate = SAMPLE_RATE_HW_HZ;
  u->hw_sample_spec.channels = 2;

  u->hw_mono_sample_spec.format = PA_SAMPLE_S16NE;
  u->hw_mono_sample_spec.rate = SAMPLE_RATE_HW_HZ;
  u->hw_mono_sample_spec.channels = 1;

  u->aep_sample_spec.format = PA_SAMPLE_S16NE;
  u->aep_sample_spec.rate = SAMPLE_RATE_AEP_HZ;
  u->aep_sample_spec.channels = 1;
  pa_channel_map_init_mono(&u->aep_channel_map);

  // The result is rounded down incorrectly thus +1
  u->aep_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->aep_sample_spec);
  u->aep_hw_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->hw_sample_spec);
  u->hw_fragment_size = pa_usec_to_bytes(PERIOD_MASTER_USECS+1, &u->hw_sample_spec);
  u->hw_fragment_size_max = max_hw_frag_size;
  if (0 != (u->hw_fragment_size_max % u->hw_fragment_size))
      u->hw_fragment_size_max += u->hw_fragment_size - (u->hw_fragment_size_max % u->hw_fragment_size);
  u->aep_hw_mono_fragment_size = pa_usec_to_bytes(PERIOD_AEP_USECS+1, &u->hw_mono_sample_spec);
  u->hw_mono_fragment_size = pa_usec_to_bytes(PERIOD_MASTER_USECS+1, &u->hw_mono_sample_spec);

  u->voice_ul_fragment_size = pa_usec_to_bytes(PERIOD_CMT_USECS+1, &u->aep_sample_spec);

  pa_silence_memchunk_get(&u->core->silence_cache,
                          u->core->mempool,
                          &u->aep_silence_memchunk,
                          &u->aep_sample_spec,
                          u->aep_fragment_size);
  voice_memchunk_pool_load(u);

  if (voice_init_raw_sink(u, raw_sink_name))
    goto fail;

  pa_sink_put(u->raw_sink);

  if (voice_init_voip_sink(u, voice_sink_name))
    goto fail;

  pa_sink_put(u->voip_sink);

  if (voice_init_aep_sink_input(u))
    goto fail;

  pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI);
  u->alt_mixer_compensation = PA_VOLUME_NORM;

  if (voice_init_hw_sink_input(u))
    goto fail;

  u->sink_temp_buff = pa_xmalloc(2 * u->hw_fragment_size_max);
  u->sink_temp_buff_len = 2 * u->hw_fragment_size_max;

  u->dl_memblockq =
          pa_memblockq_new(0, 2 * u->voice_ul_fragment_size, 0,
                           pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL);

  if (voice_init_raw_source(u, raw_source_name))
    goto fail;

  pa_source_put(u->raw_source);

  if (voice_init_voip_source(u, voice_source_name))
    goto fail;

  pa_source_put(u->voip_source);

  if (voice_init_hw_source_output(u))
    goto fail;

  u->hw_source_memblockq =
      pa_memblockq_new(0, 2 * u->hw_fragment_size_max, 0,
                       pa_frame_size(&u->hw_sample_spec), 0, 0, 0, NULL);

  u->ul_memblockq =
      pa_memblockq_new(0, 2 * u->voice_ul_fragment_size, 0,
                       pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL);

  u->cs_call_sink_input = 0;
  u->dl_sideinfo_queue = pa_queue_new();

  u->linear_q15_master_volume_L = INT16_MAX;
  u->linear_q15_master_volume_R = INT16_MAX;
  u->field_2CC = 0;

  voice_aep_ear_ref_init(u);

  if (voice_convert_init(u))
    goto fail;

  if (voice_init_event_forwarder(u, dbus_type) || voice_init_cmtspeech(u))
    goto fail;

  if (!(u->wb_mic_iir_eq = iir_eq_new(u->hw_fragment_size / 2,
                              master_source->sample_spec.channels)))
      goto fail;

  if (!(u->nb_mic_iir_eq = iir_eq_new( u->aep_fragment_size / 2, 1)))
    goto fail;

  if (!(u->wb_ear_iir_eq = fir_eq_new(master_sink->sample_spec.rate,
                                      master_sink->sample_spec.channels)))
    goto fail;

  if (!(u->nb_ear_iir_eq = iir_eq_new(u->aep_fragment_size / 2, 1)))
    goto fail;

  u->input_task_active = FALSE;
  u->xprot_watchdog = TRUE;
  u->ambient_temp = 30;
  if (!(u->xprot = xprot_new()))
    goto fail;

  u->aep_enable = FALSE;
  u->wb_meq_enable = FALSE;
  u->wb_eeq_enable = FALSE;
  u->nb_meq_enable = FALSE;
  u->nb_eeq_enable = FALSE;
  u->xprot_enable = FALSE;
  u->updating_parameters = FALSE;

  u->sink_proplist_changed_slot =
      pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED],
                              0, (pa_hook_cb_t)sink_proplist_changed_cb, u);;

  u->source_proplist_changed_slot =
      pa_hook_connect( &m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], 0,
              (pa_hook_cb_t)source_proplist_changed_cb, u);
  u->mode_accessory_hwid_hash = 0;

  p = pa_proplist_new();
  pa_proplist_sets(p, PA_NOKIA_PROP_AUDIO_MODE, "ihf");
  pa_proplist_sets(p, PA_NOKIA_PROP_AUDIO_ACCESSORY_HWID, "");

  pa_sink_update_proplist( master_sink, PA_UPDATE_REPLACE, p);

  pa_proplist_free(p);

  pa_source_output_put(u->hw_source_output);
  pa_sink_input_put(u->hw_sink_input);
  pa_sink_input_put(u->aep_sink_input);

  u->sink_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK,
                                             sink_subscribe_cb, u);

  return 0;

fail:
  if (ma)
    pa_modargs_free(ma);

  pa__done(m);

  return -1;
}
Example #19
0
int pa_oss_open(const char *device, int *mode, int* pcaps) {
    int fd = -1;
    int caps;
    char *t;

    pa_assert(device);
    pa_assert(mode);
    pa_assert(*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY);

    if(!pcaps)
        pcaps = &caps;

    if (*mode == O_RDWR) {
        if ((fd = pa_open_cloexec(device, O_RDWR|O_NDELAY, 0)) >= 0) {
            ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);

            if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
                pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
                goto fail;
            }

            if (*pcaps & DSP_CAP_DUPLEX)
                goto success;

            pa_log_warn("'%s' doesn't support full duplex", device);

            pa_close(fd);
        }

        if ((fd = pa_open_cloexec(device, (*mode = O_WRONLY)|O_NDELAY, 0)) < 0) {
            if ((fd = pa_open_cloexec(device, (*mode = O_RDONLY)|O_NDELAY, 0)) < 0) {
                pa_log("open('%s'): %s", device, pa_cstrerror(errno));
                goto fail;
            }
        }
    } else {
        if ((fd = pa_open_cloexec(device, *mode|O_NDELAY, 0)) < 0) {
            pa_log("open('%s'): %s", device, pa_cstrerror(errno));
            goto fail;
        }
    }

    *pcaps = 0;

    if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
        pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
        goto fail;
    }

success:

    t = pa_sprintf_malloc(
            "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
                 *pcaps & DSP_CAP_BATCH ? " BATCH" : "",
#ifdef DSP_CAP_BIND
                 *pcaps & DSP_CAP_BIND ? " BIND" : "",
#else
                 "",
#endif
                 *pcaps & DSP_CAP_COPROC ? " COPROC" : "",
                 *pcaps & DSP_CAP_DUPLEX ? " DUPLEX" : "",
#ifdef DSP_CAP_FREERATE
                 *pcaps & DSP_CAP_FREERATE ? " FREERATE" : "",
#else
                 "",
#endif
#ifdef DSP_CAP_INPUT
                 *pcaps & DSP_CAP_INPUT ? " INPUT" : "",
#else
                 "",
#endif
                 *pcaps & DSP_CAP_MMAP ? " MMAP" : "",
#ifdef DSP_CAP_MODEM
                 *pcaps & DSP_CAP_MODEM ? " MODEM" : "",
#else
                 "",
#endif
#ifdef DSP_CAP_MULTI
                 *pcaps & DSP_CAP_MULTI ? " MULTI" : "",
#else
                 "",
#endif
#ifdef DSP_CAP_OUTPUT
                 *pcaps & DSP_CAP_OUTPUT ? " OUTPUT" : "",
#else
                 "",
#endif
                 *pcaps & DSP_CAP_REALTIME ? " REALTIME" : "",
#ifdef DSP_CAP_SHADOW
                 *pcaps & DSP_CAP_SHADOW ? " SHADOW" : "",
#else
                 "",
#endif
#ifdef DSP_CAP_VIRTUAL
                 *pcaps & DSP_CAP_VIRTUAL ? " VIRTUAL" : "",
#else
                 "",
#endif
                 *pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");

    pa_log_debug("capabilities:%s", t);
    pa_xfree(t);

    return fd;

fail:
    if (fd >= 0)
        pa_close(fd);
    return -1;
}
Example #20
0
int pa_log_set_target(pa_log_target *t) {
    int fd = -1;
    int old_fd;

    pa_assert(t);

    switch (t->type) {
        case PA_LOG_STDERR:
        case PA_LOG_SYSLOG:
#ifdef HAVE_JOURNAL
        case PA_LOG_JOURNAL:
#endif
        case PA_LOG_NULL:
            break;
        case PA_LOG_FILE:
            if ((fd = pa_open_cloexec(t->file, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
                pa_log(_("Failed to open target file '%s'."), t->file);
                return -1;
            }
            break;
        case PA_LOG_NEWFILE: {
            char *file_path;
            char *p;
            unsigned version;

            file_path = pa_sprintf_malloc("%s.xx", t->file);
            p = file_path + strlen(t->file);

            for (version = 0; version <= LOG_MAX_SUFFIX_NUMBER; version++) {
                memset(p, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */

                if (version > 0)
                    pa_snprintf(p, 4, ".%u", version); /* Why 4? ".xx" + termitating zero byte. */

                if ((fd = pa_open_cloexec(file_path, O_WRONLY | O_TRUNC | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) >= 0)
                    break;
            }

            if (version > LOG_MAX_SUFFIX_NUMBER) {
                pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
                        t->file, t->file, t->file, t->file, LOG_MAX_SUFFIX_NUMBER);
                pa_xfree(file_path);
                return -1;
            } else
                pa_log_debug("Opened target file %s\n", file_path);

            pa_xfree(file_path);
            break;
        }
    }

    target.type = t->type;
    pa_xfree(target.file);
    target.file = pa_xstrdup(t->file);

    old_fd = log_fd;
    log_fd = fd;

    if (old_fd >= 0)
        pa_close(old_fd);

    return 0;
}
Example #21
0
int pa__init(pa_module*m) {

    struct audio_buf_info info;
    struct userdata *u = NULL;
    const char *dev;
    int fd = -1;
    int nfrags, orig_frag_size, frag_size;
    int mode, caps;
    pa_bool_t record = TRUE, playback = TRUE, use_mmap = TRUE;
    pa_sample_spec ss;
    pa_channel_map map;
    pa_modargs *ma = NULL;
    char hwdesc[64];
    const char *name;
    pa_bool_t namereg_fail;
    pa_sink_new_data sink_new_data;
    pa_source_new_data source_new_data;

    pa_assert(m);

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

    if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) {
        pa_log("record= and playback= expect boolean argument.");
        goto fail;
    }

    if (!playback && !record) {
        pa_log("Neither playback nor record enabled for device.");
        goto fail;
    }

    mode = (playback && record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0));

    ss = m->core->default_sample_spec;
    map = m->core->default_channel_map;
    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_OSS) < 0) {
        pa_log("Failed to parse sample specification or channel map");
        goto fail;
    }

    nfrags = (int) m->core->default_n_fragments;
    frag_size = (int) pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss);
    if (frag_size <= 0)
        frag_size = (int) pa_frame_size(&ss);

    if (pa_modargs_get_value_s32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_s32(ma, "fragment_size", &frag_size) < 0) {
        pa_log("Failed to parse fragments arguments");
        goto fail;
    }

    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
        pa_log("Failed to parse mmap argument.");
        goto fail;
    }

    if ((fd = pa_oss_open(dev = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), &mode, &caps)) < 0)
        goto fail;

    if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
        pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
        use_mmap = FALSE;
    }

    if (use_mmap && mode == O_WRONLY) {
        pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
        use_mmap = FALSE;
    }

    if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0)
        pa_log_info("Hardware name is '%s'.", hwdesc);
    else
        hwdesc[0] = 0;

    pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));

    orig_frag_size = frag_size;
    if (nfrags >= 2 && frag_size >= 1)
        if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
            goto fail;

    if (pa_oss_auto_format(fd, &ss) < 0)
        goto fail;

    if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) < 0) {
        pa_log("SNDCTL_DSP_GETBLKSIZE: %s", pa_cstrerror(errno));
        goto fail;
    }
    pa_assert(frag_size > 0);

    u = pa_xnew0(struct userdata, 1);
    u->core = m->core;
    u->module = m;
    m->userdata = u;
    u->fd = fd;
    u->mixer_fd = -1;
    u->mixer_devmask = 0;
    u->use_getospace = u->use_getispace = TRUE;
    u->use_getodelay = TRUE;
    u->mode = mode;
    u->frame_size = pa_frame_size(&ss);
    u->device_name = pa_xstrdup(dev);
    u->in_nfrags = u->out_nfrags = (uint32_t) (u->nfrags = nfrags);
    u->out_fragment_size = u->in_fragment_size = (uint32_t) (u->frag_size = frag_size);
    u->orig_frag_size = orig_frag_size;
    u->use_mmap = use_mmap;
    u->rtpoll = pa_rtpoll_new();
    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
    u->rtpoll_item = NULL;
    build_pollfd(u);

    if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) >= 0) {
        pa_log_info("Input -- %u fragments of size %u.", info.fragstotal, info.fragsize);
        u->in_fragment_size = (uint32_t) info.fragsize;
        u->in_nfrags = (uint32_t) info.fragstotal;
        u->use_getispace = TRUE;
    }

    if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) {
        pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize);
        u->out_fragment_size = (uint32_t) info.fragsize;
        u->out_nfrags = (uint32_t) info.fragstotal;
        u->use_getospace = TRUE;
    }

    u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size;
    u->out_hwbuf_size = u->out_nfrags * u->out_fragment_size;

    if (mode != O_WRONLY) {
        char *name_buf = NULL;

        if (use_mmap) {
            if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
                pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno));
                use_mmap = u->use_mmap = FALSE;
                u->in_mmap = NULL;
            } else
                pa_log_debug("Successfully mmap()ed input buffer.");
        }

        if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
            namereg_fail = TRUE;
        else {
            name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev));
            namereg_fail = FALSE;
        }

        pa_source_new_data_init(&source_new_data);
        source_new_data.driver = __FILE__;
        source_new_data.module = m;
        pa_source_new_data_set_name(&source_new_data, name);
        source_new_data.namereg_fail = namereg_fail;
        pa_source_new_data_set_sample_spec(&source_new_data, &ss);
        pa_source_new_data_set_channel_map(&source_new_data, &map);
        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, dev);
        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "oss");
        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev);
        pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial");
        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->in_hwbuf_size));
        pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->in_fragment_size));

        if (pa_modargs_get_proplist(ma, "source_properties", source_new_data.proplist, PA_UPDATE_REPLACE) < 0) {
            pa_log("Invalid properties");
            pa_source_new_data_done(&source_new_data);
            goto fail;
        }

        u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
        pa_source_new_data_done(&source_new_data);
        pa_xfree(name_buf);

        if (!u->source) {
            pa_log("Failed to create source object");
            goto fail;
        }

        u->source->parent.process_msg = source_process_msg;
        u->source->userdata = u;

        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
        pa_source_set_rtpoll(u->source, u->rtpoll);
        pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->in_hwbuf_size, &u->source->sample_spec));
        u->source->refresh_volume = TRUE;

        if (use_mmap)
            u->in_mmap_memblocks = pa_xnew0(pa_memblock*, u->in_nfrags);
    }
Example #22
0
/* Called from output thread context */
static int sink_input_process_msg_cb(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
    struct userdata *u = PA_SINK_INPUT(obj)->userdata;

    switch (code) {

        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
            pa_usec_t *r = data;

            pa_sink_input_assert_io_context(u->sink_input);

            *r = pa_bytes_to_usec(pa_memblockq_get_length(u->memblockq), &u->sink_input->sample_spec);

            /* Fall through, the default handler will add in the extra
             * latency added by the resampler */
            break;
        }

        case SINK_INPUT_MESSAGE_POST:

            pa_sink_input_assert_io_context(u->sink_input);

            if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
                pa_memblockq_push_align(u->memblockq, chunk);
            else
                pa_memblockq_flush_write(u->memblockq, true);

            /* Is this the end of an underrun? Then let's start things
             * right-away */
            if (!u->in_pop &&
                u->sink_input->thread_info.underrun_for > 0 &&
                pa_memblockq_is_readable(u->memblockq)) {

                pa_log_debug("Requesting rewind due to end of underrun.");
                pa_sink_input_request_rewind(u->sink_input,
                                             (size_t) (u->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : u->sink_input->thread_info.underrun_for),
                                             false, true, false);
            }

            u->recv_counter += (int64_t) chunk->length;

            return 0;

        case SINK_INPUT_MESSAGE_REWIND:

            pa_sink_input_assert_io_context(u->sink_input);

            if (PA_SINK_IS_OPENED(u->sink_input->sink->thread_info.state))
                pa_memblockq_seek(u->memblockq, -offset, PA_SEEK_RELATIVE, true);
            else
                pa_memblockq_flush_write(u->memblockq, true);

            u->recv_counter -= offset;

            return 0;

        case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: {
            size_t length;

            length = pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq);

            u->latency_snapshot.recv_counter = u->recv_counter;
            u->latency_snapshot.sink_input_buffer = pa_memblockq_get_length(u->memblockq);
            /* Add content of render memblockq to sink latency */
            u->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(u->sink_input->sink) +
                                               pa_bytes_to_usec(length, &u->sink_input->sink->sample_spec);
            u->latency_snapshot.sink_timestamp = pa_rtclock_now();

            return 0;
        }
    }

    return pa_sink_input_process_msg(obj, code, data, offset, chunk);
}
Example #23
0
static void thread_func(void *userdata) {
    struct userdata *u = userdata;
    int write_type = 0, read_type = 0;
    short revents = 0;

    pa_assert(u);

    pa_log_debug("Thread starting up");

    if (u->core->realtime_scheduling)
        pa_make_realtime(u->core->realtime_priority);

    pa_thread_mq_install(&u->thread_mq);

    for (;;) {
        int ret;

/*        pa_log("loop");    */

        if (PA_UNLIKELY(u->sink && u->sink->thread_info.rewind_requested))
            pa_sink_process_rewind(u->sink, 0);

        /* Render some data and write it to the dsp */

        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {

            if (u->use_mmap) {

                if ((ret = mmap_write(u)) < 0)
                    goto fail;

                revents &= ~POLLOUT;

                if (ret > 0)
                    continue;

            } else {
                ssize_t l;
                pa_bool_t loop = FALSE, work_done = FALSE;

                l = (ssize_t) u->out_fragment_size;

                if (u->use_getospace) {
                    audio_buf_info info;

                    if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) {
                        pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno));
                        u->use_getospace = FALSE;
                    } else {
                        l = info.bytes;

                        /* We loop only if GETOSPACE worked and we
                         * actually *know* that we can write more than
                         * one fragment at a time */
                        loop = TRUE;
                    }
                }

                /* Round down to multiples of the fragment size,
                 * because OSS needs that (at least some versions
                 * do) */
                l = (l/(ssize_t) u->out_fragment_size) * (ssize_t) u->out_fragment_size;

                /* Hmm, so poll() signalled us that we can read
                 * something, but GETOSPACE told us there was nothing?
                 * Hmm, make the best of it, try to read some data, to
                 * avoid spinning forever. */
                if (l <= 0 && (revents & POLLOUT)) {
                    l = (ssize_t) u->out_fragment_size;
                    loop = FALSE;
                }

                while (l > 0) {
                    void *p;
                    ssize_t t;

                    if (u->memchunk.length <= 0)
                        pa_sink_render(u->sink, (size_t) l, &u->memchunk);

                    pa_assert(u->memchunk.length > 0);

                    p = pa_memblock_acquire(u->memchunk.memblock);
                    t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type);
                    pa_memblock_release(u->memchunk.memblock);

/*                     pa_log("wrote %i bytes of %u", t, l); */

                    pa_assert(t != 0);

                    if (t < 0) {

                        if (errno == EINTR)
                            continue;

                        else if (errno == EAGAIN) {
                            pa_log_debug("EAGAIN");

                            revents &= ~POLLOUT;
                            break;

                        } else {
                            pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {

                        u->memchunk.index += (size_t) t;
                        u->memchunk.length -= (size_t) t;

                        if (u->memchunk.length <= 0) {
                            pa_memblock_unref(u->memchunk.memblock);
                            pa_memchunk_reset(&u->memchunk);
                        }

                        l -= t;

                        revents &= ~POLLOUT;
                        work_done = TRUE;
                    }

                    if (!loop)
                        break;
                }

                if (work_done)
                    continue;
            }
        }

        /* Try to read some data and pass it on to the source driver. */

        if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {

            if (u->use_mmap) {

                if ((ret = mmap_read(u)) < 0)
                    goto fail;

                revents &= ~POLLIN;

                if (ret > 0)
                    continue;

            } else {

                void *p;
                ssize_t l;
                pa_memchunk memchunk;
                pa_bool_t loop = FALSE, work_done = FALSE;

                l = (ssize_t) u->in_fragment_size;

                if (u->use_getispace) {
                    audio_buf_info info;

                    if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) {
                        pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno));
                        u->use_getispace = FALSE;
                    } else {
                        l = info.bytes;
                        loop = TRUE;
                    }
                }

                l = (l/(ssize_t) u->in_fragment_size) * (ssize_t) u->in_fragment_size;

                if (l <= 0 && (revents & POLLIN)) {
                    l = (ssize_t) u->in_fragment_size;
                    loop = FALSE;
                }

                while (l > 0) {
                    ssize_t t;
                    size_t k;

                    pa_assert(l > 0);

                    memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);

                    k = pa_memblock_get_length(memchunk.memblock);

                    if (k > (size_t) l)
                        k = (size_t) l;

                    k = (k/u->frame_size)*u->frame_size;

                    p = pa_memblock_acquire(memchunk.memblock);
                    t = pa_read(u->fd, p, k, &read_type);
                    pa_memblock_release(memchunk.memblock);

                    pa_assert(t != 0); /* EOF cannot happen */

/*                     pa_log("read %i bytes of %u", t, l); */

                    if (t < 0) {
                        pa_memblock_unref(memchunk.memblock);

                        if (errno == EINTR)
                            continue;

                        else if (errno == EAGAIN) {
                            pa_log_debug("EAGAIN");

                            revents &= ~POLLIN;
                            break;

                        } else {
                            pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno));
                            goto fail;
                        }

                    } else {
                        memchunk.index = 0;
                        memchunk.length = (size_t) t;

                        pa_source_post(u->source, &memchunk);
                        pa_memblock_unref(memchunk.memblock);

                        l -= t;

                        revents &= ~POLLIN;
                        work_done = TRUE;
                    }

                    if (!loop)
                        break;
                }

                if (work_done)
                    continue;
            }
        }

/*         pa_log("loop2 revents=%i", revents); */

        if (u->rtpoll_item) {
            struct pollfd *pollfd;

            pa_assert(u->fd >= 0);

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
            pollfd->events = (short)
                (((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
                 ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0));
        }

        /* Hmm, nothing to do. Let's sleep */
        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
            goto fail;

        if (ret == 0)
            goto finish;

        if (u->rtpoll_item) {
            struct pollfd *pollfd;

            pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

            if (pollfd->revents & ~(POLLOUT|POLLIN)) {
                pa_log("DSP shutdown.");
                goto fail;
            }

            revents = pollfd->revents;
        } else
            revents = 0;
    }

fail:
    /* If this was no regular exit from the loop we have to continue
     * processing messages until we received PA_MESSAGE_SHUTDOWN */
    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);

finish:
    pa_log_debug("Thread shutting down");
}
static void resolver_cb(
        AvahiServiceResolver *r,
        AvahiIfIndex interface, AvahiProtocol protocol,
        AvahiResolverEvent event,
        const char *name, const char *type, const char *domain,
        const char *host_name, const AvahiAddress *a, uint16_t port,
        AvahiStringList *txt,
        AvahiLookupResultFlags flags,
        void *userdata) {

    struct userdata *u = userdata;
    struct tunnel *tnl;

    pa_assert(u);

    tnl = tunnel_new(interface, protocol, name, type, domain);

    if (event != AVAHI_RESOLVER_FOUND)
        pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
    else {
        char *device = NULL, *dname, *module_name, *args;
        const char *t;
        char at[AVAHI_ADDRESS_STR_MAX], cmt[PA_CHANNEL_MAP_SNPRINT_MAX];
        pa_sample_spec ss;
        pa_channel_map cm;
        AvahiStringList *l;
        pa_bool_t channel_map_set = FALSE;
        pa_module *m;

        ss = u->core->default_sample_spec;
        cm = u->core->default_channel_map;

        for (l = txt; l; l = l->next) {
            char *key, *value;
            pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);

            if (pa_streq(key, "device")) {
                pa_xfree(device);
                device = value;
                value = NULL;
            } else if (pa_streq(key, "rate"))
                ss.rate = (uint32_t) atoi(value);
            else if (pa_streq(key, "channels"))
                ss.channels = (uint8_t) atoi(value);
            else if (pa_streq(key, "format"))
                ss.format = pa_parse_sample_format(value);
            else if (pa_streq(key, "channel_map")) {
                pa_channel_map_parse(&cm, value);
                channel_map_set = TRUE;
            }

            avahi_free(key);
            avahi_free(value);
        }

        if (!channel_map_set && cm.channels != ss.channels)
            pa_channel_map_init_extend(&cm, ss.channels, PA_CHANNEL_MAP_DEFAULT);

        if (!pa_sample_spec_valid(&ss)) {
            pa_log("Service '%s' contains an invalid sample specification.", name);
            avahi_free(device);
            goto finish;
        }

        if (!pa_channel_map_valid(&cm) || cm.channels != ss.channels) {
            pa_log("Service '%s' contains an invalid channel map.", name);
            avahi_free(device);
            goto finish;
        }

        if (device)
            dname = pa_sprintf_malloc("tunnel.%s.%s", host_name, device);
        else
            dname = pa_sprintf_malloc("tunnel.%s", host_name);

        if (!pa_namereg_is_valid_name(dname)) {
            pa_log("Cannot construct valid device name from credentials of service '%s'.", dname);
            avahi_free(device);
            pa_xfree(dname);
            goto finish;
        }

        t = strstr(type, "sink") ? "sink" : "source";

        module_name = pa_sprintf_malloc("module-tunnel-%s", t);
        args = pa_sprintf_malloc("server=[%s]:%u "
                                 "%s=%s "
                                 "format=%s "
                                 "channels=%u "
                                 "rate=%u "
                                 "%s_name=%s "
                                 "channel_map=%s",
                                 avahi_address_snprint(at, sizeof(at), a), port,
                                 t, device,
                                 pa_sample_format_to_string(ss.format),
                                 ss.channels,
                                 ss.rate,
                                 t, dname,
                                 pa_channel_map_snprint(cmt, sizeof(cmt), &cm));

        pa_log_debug("Loading %s with arguments '%s'", module_name, args);

        if ((m = pa_module_load(u->core, module_name, args))) {
            tnl->module_index = m->index;
            pa_hashmap_put(u->tunnels, tnl, tnl);
            tnl = NULL;
        }

        pa_xfree(module_name);
        pa_xfree(dname);
        pa_xfree(args);
        avahi_free(device);
    }

finish:

    avahi_service_resolver_free(r);

    if (tnl)
        tunnel_free(tnl);
}
Example #25
0
pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
                           pa_sample_spec *source_ss, pa_channel_map *source_map,
                           pa_sample_spec *sink_ss, pa_channel_map *sink_map,
                           uint32_t *nframes, const char *args)
{
    int rate;
    uint32_t y, frame_size_ms, filter_size_ms;
    pa_modargs *ma;

    if (!(ma = pa_modargs_new(args, valid_modargs))) {
        pa_log("Failed to parse submodule arguments.");
        goto fail;
    }

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

    frame_size_ms = DEFAULT_FRAME_SIZE_MS;
    if (pa_modargs_get_value_u32(ma, "frame_size_ms", &frame_size_ms) < 0 || frame_size_ms < 1 || frame_size_ms > 200) {
        pa_log("Invalid frame_size_ms specification");
        goto fail;
    }

    pa_speex_ec_fixate_spec(source_ss, source_map, sink_ss, sink_map);

    rate = source_ss->rate;
    *nframes = (rate * frame_size_ms) / 1000;
    /* nframes should be a power of 2, round down to nearest power of two */
    y = 1 << ((8 * sizeof (uint32_t)) - 2);
    while (y > *nframes)
      y >>= 1;
    *nframes = y;

    pa_log_debug ("Using nframes %d, channels %d, rate %d", *nframes, source_ss->channels, source_ss->rate);

    ec->params.priv.speex.state = speex_echo_state_init_mc (*nframes, (rate * filter_size_ms) / 1000, source_ss->channels, source_ss->channels);

    if (!ec->params.priv.speex.state)
        goto fail;

    speex_echo_ctl(ec->params.priv.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate);

    if (!pa_speex_ec_preprocessor_init(ec, source_ss, *nframes, ma))
        goto fail;

    pa_modargs_free(ma);
    return TRUE;

fail:
    if (ma)
        pa_modargs_free(ma);
    if (ec->params.priv.speex.pp_state) {
        speex_preprocess_state_destroy(ec->params.priv.speex.pp_state);
        ec->params.priv.speex.pp_state = NULL;
    }
    if (ec->params.priv.speex.state) {
        speex_echo_state_destroy(ec->params.priv.speex.state);
        ec->params.priv.speex.state = NULL;
    }
    return FALSE;
}
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
    struct userdata *u = userdata;

    pa_assert(c);
    pa_assert(u);

    u->client = c;

    switch (state) {
        case AVAHI_CLIENT_S_REGISTERING:
        case AVAHI_CLIENT_S_RUNNING:
        case AVAHI_CLIENT_S_COLLISION:

            if (!u->sink_browser) {

                if (!(u->sink_browser = avahi_service_browser_new(
                              c,
                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
                              SERVICE_TYPE_SINK,
                              NULL,
                              0,
                              browser_cb, u))) {

                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
                    pa_module_unload_request(u->module, TRUE);
                }
            }

            if (!u->source_browser) {

                if (!(u->source_browser = avahi_service_browser_new(
                              c,
                              AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
                              SERVICE_TYPE_SOURCE,
                              NULL,
                              0,
                              browser_cb, u))) {

                    pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
                    pa_module_unload_request(u->module, TRUE);
                }
            }

            break;

        case AVAHI_CLIENT_FAILURE:
            if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
                int error;

                pa_log_debug("Avahi daemon disconnected.");

                if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
                    pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
                    pa_module_unload_request(u->module, TRUE);
                }
            }

            /* Fall through */

        case AVAHI_CLIENT_CONNECTING:

            if (u->sink_browser) {
                avahi_service_browser_free(u->sink_browser);
                u->sink_browser = NULL;
            }

            if (u->source_browser) {
                avahi_service_browser_free(u->source_browser);
                u->source_browser = NULL;
            }

            break;

        default: ;
    }
}
static void pid_hash_insert(struct pa_classify_pid_hash **hash, pid_t pid,
                            const char *prop, enum pa_classify_method method,
                            const char *arg, const char *group)
{
    struct pa_classify_pid_hash *st;
    struct pa_classify_pid_hash *prev;

    pa_assert(hash);
    pa_assert(group);

    if ((st = pid_hash_find(hash, pid, prop,method,arg, &prev))) {
        pa_xfree(st->group);
        st->group = pa_xstrdup(group);

        pa_log_debug("pid hash group changed (%u|%s|%s|%s|%s)", st->pid,
                     st->prop ? st->prop : "", method_str(st->method.type),
                     st->arg.def ? st->arg.def : "", st->group);
    }
    else {
        st  = pa_xnew0(struct pa_classify_pid_hash, 1);

        st->next  = prev->next;
        st->pid   = pid;
        st->prop  = prop ? pa_xstrdup(prop) : NULL;
        st->group = pa_xstrdup(group);

        if (!prop)
            st->arg.def = pa_xstrdup("");
        else {
            st->method.type = method;

            switch (method) {

            case pa_method_equals:
                st->method.func = pa_classify_method_equals;
                st->arg.value.string = st->arg.def = pa_xstrdup(arg ? arg:"");
                break;

            case pa_method_startswith:
                st->method.func = pa_classify_method_startswith;
                st->arg.value.string = st->arg.def = pa_xstrdup(arg ? arg:"");
                break;

            case pa_method_matches:
                st->method.func = pa_classify_method_matches;
                st->arg.def = pa_xstrdup(arg ? arg:"");
                if (!arg || regcomp(&st->arg.value.rexp, arg, 0) != 0) {
                    st->method.type = pa_method_true;
                    st->method.func = pa_classify_method_true;
                }
                break;

            default:
            case pa_method_true:
                st->method.func = pa_classify_method_true;
                break;
            }
        }

        prev->next = st;

        pa_log_debug("pid hash added (%u|%s|%s|%s|%s)", st->pid,
                     st->prop ? st->prop : "", method_str(st->method.type),
                     st->arg.def ? st->arg.def : "", st->group);
    }
}
static int context_autospawn(pa_context *c) {
    pid_t pid;
    int status, r;
    struct sigaction sa;

    pa_context_ref(c);

    if (sigaction(SIGCHLD, NULL, &sa) < 0) {
        pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
        pa_context_fail(c, PA_ERR_INTERNAL);
        goto fail;
    }

#ifdef SA_NOCLDWAIT
    if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
#else
    if (sa.sa_handler == SIG_IGN) {
#endif
        pa_log_debug("Process disabled waitpid(), cannot autospawn.");
        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto fail;
    }

    pa_log_debug("Trying to autospawn...");

    if (c->spawn_api.prefork)
        c->spawn_api.prefork();

    if ((pid = fork()) < 0) {
        pa_log_error(_("fork(): %s"), pa_cstrerror(errno));
        pa_context_fail(c, PA_ERR_INTERNAL);

        if (c->spawn_api.postfork)
            c->spawn_api.postfork();

        goto fail;
    } else if (!pid) {
        /* Child */

        const char *state = NULL;
        const char * argv[32];
        unsigned n = 0;

        if (c->spawn_api.atfork)
            c->spawn_api.atfork();

        /* We leave most of the cleaning up of the process environment
         * to the executable. We only clean up the file descriptors to
         * make sure the executable can actually be loaded
         * correctly. */
        pa_close_all(-1);

        /* Setup argv */
        argv[n++] = c->conf->daemon_binary;
        argv[n++] = "--start";

        while (n < PA_ELEMENTSOF(argv)-1) {
            char *a;

            if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
                break;

            argv[n++] = a;
        }

        argv[n++] = NULL;
        pa_assert(n <= PA_ELEMENTSOF(argv));

        execv(argv[0], (char * const *) argv);
        _exit(1);
    }

    /* Parent */

    if (c->spawn_api.postfork)
        c->spawn_api.postfork();

    do {
        r = waitpid(pid, &status, 0);
    } while (r < 0 && errno == EINTR);

    if (r < 0) {

        if (errno != ESRCH) {
            pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
            pa_context_fail(c, PA_ERR_INTERNAL);
            goto fail;
        }

        /* hmm, something already reaped our child, so we assume
         * startup worked, even if we cannot know */

    } else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
        goto fail;
    }

    pa_context_unref(c);

    return 0;

fail:

    pa_context_unref(c);

    return -1;
}

#endif /* OS_IS_WIN32 */

static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata);

#ifdef HAVE_DBUS
static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wrap_connection **conn) {
    DBusError error;

    pa_assert(c);
    pa_assert(conn);

    dbus_error_init(&error);

    if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) {
        pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message);
        goto fail;
    }

    if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) {
        pa_log_warn("Failed to add filter function");
        goto fail;
    }
    c->filter_added = true;

    if (pa_dbus_add_matches(
                pa_dbus_wrap_connection_get(*conn), &error,
                "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0) {

        pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message);
        goto fail;
    }

    return;

fail:
    if (*conn) {
        pa_dbus_wrap_connection_free(*conn);
        *conn = NULL;
    }

    dbus_error_free(&error);
}
static struct pa_classify_stream_def *
streams_find(struct pa_classify_stream_def **defs, pa_proplist *proplist,
             const char *clnam, const char *sname, uid_t uid, const char *exe,
             struct pa_classify_stream_def **prev_ret)
{
#define PROPERTY_MATCH     (!d->prop || !d->method || \
                           (d->method && d->method(prv, &d->arg)))
#define STRING_MATCH_OF(m) (!d->m || (m && d->m && !strcmp(m, d->m)))
#define ID_MATCH_OF(m)     (d->m == -1 || m == d->m)

    struct pa_classify_stream_def *prev;
    struct pa_classify_stream_def *d;
    char *prv;

    for (prev = (struct pa_classify_stream_def *)defs;
         (d = prev->next) != NULL;
         prev = prev->next)
    {
        if (!proplist || !d->prop ||
            !(prv = (char *)pa_proplist_gets(proplist, d->prop)) || !prv[0])
        {
            prv = (char *)"<unknown>";
        }

#if 0
        if (d->method == pa_classify_method_matches) {
            pa_log_debug("%s: prv='%s' prop='%s' arg=<regexp>",
                         __FUNCTION__, prv, d->prop?d->prop:"<null>");
        }
        else {
            pa_log_debug("%s: prv='%s' prop='%s' arg='%s'",
                         __FUNCTION__, prv, d->prop?d->prop:"<null>",
                         d->arg.string?d->arg.string:"<null>");
        }
#endif

        if (PROPERTY_MATCH         &&
            STRING_MATCH_OF(clnam) &&
            ID_MATCH_OF(uid)       &&
            /* case for dynamically changing active sink. */
            (!sname || (sname && d->sname && !strcmp(sname, d->sname))) &&
            (d->sact == -1 || d->sact == 1) &&
            /* end special case */
            STRING_MATCH_OF(exe)      )
            break;

    }

    if (prev_ret)
        *prev_ret = prev;

#if 0
    {
        char *s = pa_proplist_to_string_sep(proplist, " ");
        pa_log_debug("%s(<%s>,'%s',%d,'%s') => %p", __FUNCTION__,
                     s, clnam?clnam:"<null>", uid, exe?exe:"<null>", d);
        pa_xfree(s);
    }
#endif

    return d;

#undef STRING_MATCH_OF
#undef ID_MATCH_OF
}
Example #30
0
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
    pa_context *c = userdata;

    pa_assert(pd);
    pa_assert(c);
    pa_assert(c->state == PA_CONTEXT_AUTHORIZING || c->state == PA_CONTEXT_SETTING_NAME);

    pa_context_ref(c);

    if (command != PA_COMMAND_REPLY) {
        pa_context_handle_error(c, command, t, TRUE);
        goto finish;
    }

    switch(c->state) {
        case PA_CONTEXT_AUTHORIZING: {
            pa_tagstruct *reply;
            pa_bool_t shm_on_remote = FALSE;

            if (pa_tagstruct_getu32(t, &c->version) < 0 ||
                !pa_tagstruct_eof(t)) {
                pa_context_fail(c, PA_ERR_PROTOCOL);
                goto finish;
            }

            /* Minimum supported version */
            if (c->version < 8) {
                pa_context_fail(c, PA_ERR_VERSION);
                goto finish;
            }

            /* Starting with protocol version 13 the MSB of the version
               tag reflects if shm is available for this connection or
               not. */
            if (c->version >= 13) {
                shm_on_remote = !!(c->version & 0x80000000U);
                c->version &= 0x7FFFFFFFU;
            }

            pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);

            /* Enable shared memory support if possible */
            if (c->do_shm)
                if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
                    c->do_shm = FALSE;

            if (c->do_shm) {

                /* Only enable SHM if both sides are owned by the same
                 * user. This is a security measure because otherwise
                 * data private to the user might leak. */

#ifdef HAVE_CREDS
                const pa_creds *creds;
                if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
                    c->do_shm = FALSE;
#endif
            }

            pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
            pa_pstream_enable_shm(c->pstream, c->do_shm);

            reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);

            if (c->version >= 13) {
                pa_init_proplist(c->proplist);
                pa_tagstruct_put_proplist(reply, c->proplist);
            } else
                pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));

            pa_pstream_send_tagstruct(c->pstream, reply);
            pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);

            pa_context_set_state(c, PA_CONTEXT_SETTING_NAME);
            break;
        }

        case PA_CONTEXT_SETTING_NAME :

            if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
                                      c->client_index == PA_INVALID_INDEX)) ||
                !pa_tagstruct_eof(t)) {
                pa_context_fail(c, PA_ERR_PROTOCOL);
                goto finish;
            }

            pa_context_set_state(c, PA_CONTEXT_READY);
            break;

        default:
            pa_assert_not_reached();
    }

finish:
    pa_context_unref(c);
}