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; }
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); }
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); }
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; }
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; }
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; }
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; }
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 = ∩︀ 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; }
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; }
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); }
/* 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); }
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); }
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 }
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); }