mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-18 06:59:57 -05:00
Yes, yet another evil all-in-one commit of intervowen changes. I suck.
* Drop "state" directory, fold that into "runtime directory" * No longer automatically rewind when a new stream connects * Rework sound file stream, to cause a rewind on initialisation, shorten _pop() code a bit * Fix reference counting of pa_socket_server in the protocol implementations * Rework daemon initialization code to be compatible with non-SUID-root setups where RLIMIT_RTPRIO is non-zero * Print warning if RT/HP is enabled in the config, but due to missing caps, rlimits, policy we cannot enable it. * Fix potential memory leak in pa_open_config_file() * Add pa_find_config_file() which works much like pa_open_config_file() but doesn't actually open the config file in question. Just searches for it. * Add portable pa_is_path_absolute() * Add pa_close_all() and use it on daemon startup to close leaking file descriptors (inspired from what I did for libdaemon) * Add pa_unblock_sigs() and use it on daemon startup to unblock all signals (inspired from libdaemon, too) * Add pa_reset_sigs() and use it on daemon startup to reset all signal handlers (inspired from libdaemon as well) * Implement pa_set_env() * Define RLIMIT_RTTIME and friends if not defined by glibc * Add pa_strempty() * rename state testing macros to include _IS_, to make clearer that they are no states, but testing macros * Implement pa_source_output_set_requested_latency_within_thread() to be able to forward latency info to sources from within the IO thread * Similar for sink inputs * generelize since_underrun counter in sink inputs to "playing_for" and "underrun_for". Use only this for ignore potential rewind requests over underruns * Add new native protocol message PLAYBACK_STREAM_MESSAGE_STARTED for notification about the end of an underrun * Port native protocol to use underrun_for/playing_for which is maintained by the sink input anyway * Pass underrun_for/playing_for in timing info to client * Drop pa_sink_skip() since it breaks underrun detection code * Move PID file and unix sockets to the runtime dir (i.e. ~/.pulse). This fixes a potention DoS attack from other users stealing dirs in /tmp from us so that we cannot take them anymore) * Allow setting of more resource limits from the config file. Set RTTIME by default * Streamline daemon startup code * Rework algorithm to find default configuration files * If run in system mode use "system.pa" instead of "default.pa" as default script file * Change ladspa sink to use pa_clamp_samples() for clamping samples * Teach module-null-sink how to deal with rewinding * Try to support ALSA devices with no implicit channel map. Synthesize one by padding with PA_CHANNEL_POSITION_AUX channels. This is not tested since I lack hardware with these problems. * Make use of time smoother in the client libraries. * Add new pa_stream_is_corked() and pa_stream_set_started_callback() functions to public API * Since our native socket moved, add some code for finding sockets created by old versions of PA. This should ease upgrades git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2329 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
parent
f94fae3da3
commit
52e3628c3e
48 changed files with 2408 additions and 1232 deletions
|
|
@ -671,8 +671,24 @@ snd_pcm_t *pa_alsa_open_by_device_string(
|
|||
*dev = d;
|
||||
|
||||
if (ss->channels != map->channels) {
|
||||
pa_assert_se(pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_AUX));
|
||||
pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA);
|
||||
if (!pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA)) {
|
||||
unsigned c;
|
||||
pa_channel_position_t pos;
|
||||
|
||||
pa_log_warn("Device has an unknown channel mapping. This is a limitation of ALSA. Synthesizing channel map.");
|
||||
|
||||
for (c = ss->channels; c > 0; c--)
|
||||
if (pa_channel_map_init_auto(map, c, PA_CHANNEL_MAP_ALSA))
|
||||
break;
|
||||
|
||||
pa_assert(c > 0);
|
||||
|
||||
pos = PA_CHANNEL_POSITION_AUX0;
|
||||
for (; c < map->channels; c ++)
|
||||
map->map[c] = pos++;
|
||||
|
||||
map->channels = ss->channels;
|
||||
}
|
||||
}
|
||||
|
||||
return pcm_handle;
|
||||
|
|
|
|||
|
|
@ -665,7 +665,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
|
||||
|
||||
case PA_SINK_SUSPENDED:
|
||||
pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
|
||||
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
|
||||
|
||||
if (suspend(u) < 0)
|
||||
return -1;
|
||||
|
|
@ -836,9 +836,20 @@ static int sink_set_mute_cb(pa_sink *s) {
|
|||
|
||||
static void sink_update_requested_latency_cb(pa_sink *s) {
|
||||
struct userdata *u = s->userdata;
|
||||
snd_pcm_sframes_t before;
|
||||
pa_assert(u);
|
||||
|
||||
before = u->hwbuf_unused_frames;
|
||||
update_sw_params(u);
|
||||
|
||||
/* Let's check whether we now use only a smaller part of the
|
||||
buffer then before. If so, we need to make sure that subsequent
|
||||
rewinds are relative to the new maxium fill level and not to the
|
||||
current fill level. Thus, let's do a full rewind once, to clear
|
||||
things up. */
|
||||
|
||||
if (u->hwbuf_unused_frames > before)
|
||||
pa_sink_request_rewind(s, 0);
|
||||
}
|
||||
|
||||
static int process_rewind(struct userdata *u) {
|
||||
|
|
@ -846,6 +857,7 @@ static int process_rewind(struct userdata *u) {
|
|||
size_t rewind_nbytes, unused_nbytes, limit_nbytes;
|
||||
pa_assert(u);
|
||||
|
||||
/* Figure out how much we shall rewind and reset the counter */
|
||||
rewind_nbytes = u->sink->thread_info.rewind_nbytes;
|
||||
u->sink->thread_info.rewind_nbytes = 0;
|
||||
|
||||
|
|
@ -917,7 +929,7 @@ static void thread_func(void *userdata) {
|
|||
/* pa_log_debug("loop"); */
|
||||
|
||||
/* Render some data and write it to the dsp */
|
||||
if (PA_SINK_OPENED(u->sink->thread_info.state)) {
|
||||
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
|
||||
int work_done = 0;
|
||||
|
||||
if (u->sink->thread_info.rewind_nbytes > 0)
|
||||
|
|
@ -982,7 +994,7 @@ static void thread_func(void *userdata) {
|
|||
goto finish;
|
||||
|
||||
/* Tell ALSA about this and process its response */
|
||||
if (PA_SINK_OPENED(u->sink->thread_info.state)) {
|
||||
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
|
||||
struct pollfd *pollfd;
|
||||
unsigned short revents = 0;
|
||||
int err;
|
||||
|
|
|
|||
|
|
@ -621,7 +621,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
|
|||
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
|
||||
|
||||
case PA_SOURCE_SUSPENDED:
|
||||
pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
|
||||
pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
|
||||
|
||||
if (suspend(u) < 0)
|
||||
return -1;
|
||||
|
|
@ -819,7 +819,7 @@ static void thread_func(void *userdata) {
|
|||
pa_log_debug("loop");
|
||||
|
||||
/* Read some data and pass it to the sources */
|
||||
if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
|
||||
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
|
||||
int work_done = 0;
|
||||
|
||||
if (u->use_mmap)
|
||||
|
|
@ -867,7 +867,7 @@ static void thread_func(void *userdata) {
|
|||
goto finish;
|
||||
|
||||
/* Tell ALSA about this and process its response */
|
||||
if (PA_SOURCE_OPENED(u->source->thread_info.state)) {
|
||||
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
|
||||
struct pollfd *pollfd;
|
||||
unsigned short revents = 0;
|
||||
int err;
|
||||
|
|
|
|||
|
|
@ -162,13 +162,13 @@ static void adjust_rates(struct userdata *u) {
|
|||
if (!u->master)
|
||||
return;
|
||||
|
||||
if (!PA_SINK_OPENED(pa_sink_get_state(u->sink)))
|
||||
if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
|
||||
return;
|
||||
|
||||
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
|
||||
pa_usec_t sink_latency;
|
||||
|
||||
if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
|
||||
if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
|
||||
continue;
|
||||
|
||||
sink_latency = pa_sink_get_latency(o->sink);
|
||||
|
|
@ -194,7 +194,7 @@ static void adjust_rates(struct userdata *u) {
|
|||
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
|
||||
uint32_t r = base_rate;
|
||||
|
||||
if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink)))
|
||||
if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
|
||||
continue;
|
||||
|
||||
if (o->total_latency < target_latency)
|
||||
|
|
@ -258,7 +258,10 @@ static void thread_func(void *userdata) {
|
|||
pa_rtclock_get(&now);
|
||||
|
||||
if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) {
|
||||
pa_sink_skip(u->sink, u->block_size);
|
||||
pa_memchunk chunk;
|
||||
|
||||
pa_sink_render_full(u->sink, u->block_size, &chunk);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
|
||||
if (!u->thread_info.in_null_mode)
|
||||
u->thread_info.timestamp = now;
|
||||
|
|
@ -432,7 +435,7 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64
|
|||
|
||||
case SINK_INPUT_MESSAGE_POST:
|
||||
|
||||
if (PA_SINK_OPENED(o->sink_input->sink->thread_info.state))
|
||||
if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state))
|
||||
pa_memblockq_push_align(o->memblockq, chunk);
|
||||
else
|
||||
pa_memblockq_flush(o->memblockq);
|
||||
|
|
@ -471,7 +474,7 @@ static void enable_output(struct output *o) {
|
|||
|
||||
pa_sink_input_put(o->sink_input);
|
||||
|
||||
if (o->userdata->sink && PA_SINK_LINKED(pa_sink_get_state(o->userdata->sink)))
|
||||
if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink)))
|
||||
pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
|
@ -504,7 +507,7 @@ static void unsuspend(struct userdata *u) {
|
|||
|
||||
pa_sink_suspend(o->sink, FALSE);
|
||||
|
||||
if (PA_SINK_OPENED(pa_sink_get_state(o->sink)))
|
||||
if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
|
||||
enable_output(o);
|
||||
}
|
||||
|
||||
|
|
@ -525,7 +528,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
|
|||
|
||||
switch (state) {
|
||||
case PA_SINK_SUSPENDED:
|
||||
pa_assert(PA_SINK_OPENED(pa_sink_get_state(u->sink)));
|
||||
pa_assert(PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
|
||||
|
||||
suspend(u);
|
||||
break;
|
||||
|
|
@ -697,7 +700,7 @@ static void pick_master(struct userdata *u, struct output *except) {
|
|||
if (u->master &&
|
||||
u->master != except &&
|
||||
u->master->sink_input &&
|
||||
PA_SINK_OPENED(pa_sink_get_state(u->master->sink))) {
|
||||
PA_SINK_IS_OPENED(pa_sink_get_state(u->master->sink))) {
|
||||
update_master(u, u->master);
|
||||
return;
|
||||
}
|
||||
|
|
@ -705,7 +708,7 @@ static void pick_master(struct userdata *u, struct output *except) {
|
|||
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
|
||||
if (o != except &&
|
||||
o->sink_input &&
|
||||
PA_SINK_OPENED(pa_sink_get_state(o->sink))) {
|
||||
PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) {
|
||||
update_master(u, o);
|
||||
return;
|
||||
}
|
||||
|
|
@ -780,7 +783,7 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
|
|||
|
||||
pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
|
||||
|
||||
if (u->sink && PA_SINK_LINKED(pa_sink_get_state(u->sink)))
|
||||
if (u->sink && PA_SINK_IS_LINKED(pa_sink_get_state(u->sink)))
|
||||
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
|
||||
else {
|
||||
/* If the sink is not yet started, we need to do the activation ourselves */
|
||||
|
|
@ -792,10 +795,10 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
|
|||
o->outq);
|
||||
}
|
||||
|
||||
if (PA_SINK_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
|
||||
if (PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) {
|
||||
pa_sink_suspend(sink, FALSE);
|
||||
|
||||
if (PA_SINK_OPENED(pa_sink_get_state(sink)))
|
||||
if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
|
||||
if (output_create_sink_input(o) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
|
@ -898,7 +901,7 @@ static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struc
|
|||
|
||||
state = pa_sink_get_state(s);
|
||||
|
||||
if (PA_SINK_OPENED(state) && PA_SINK_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
|
||||
if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) {
|
||||
enable_output(o);
|
||||
pick_master(u, NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2006 Lennart Poettering
|
||||
Copyright 2006-2008 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
|
|
@ -25,10 +25,16 @@
|
|||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <pulse/timeval.h>
|
||||
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/module.h>
|
||||
#include <pulsecore/log.h>
|
||||
#include <pulsecore/namereg.h>
|
||||
#include <pulsecore/core-error.h>
|
||||
|
||||
#include "module-default-device-restore-symdef.h"
|
||||
|
||||
|
|
@ -39,15 +45,24 @@ PA_MODULE_LOAD_ONCE(TRUE);
|
|||
|
||||
#define DEFAULT_SINK_FILE "default-sink"
|
||||
#define DEFAULT_SOURCE_FILE "default-source"
|
||||
#define DEFAULT_SAVE_INTERVAL 5
|
||||
|
||||
int pa__init(pa_module *m) {
|
||||
struct userdata {
|
||||
pa_core *core;
|
||||
pa_subscription *subscription;
|
||||
pa_time_event *time_event;
|
||||
char *sink_filename, *source_filename;
|
||||
pa_bool_t modified;
|
||||
};
|
||||
|
||||
static void load(struct userdata *u) {
|
||||
FILE *f;
|
||||
|
||||
/* We never overwrite manually configured settings */
|
||||
|
||||
if (m->core->default_sink_name)
|
||||
if (u->core->default_sink_name)
|
||||
pa_log_info("Manually configured default sink, not overwriting.");
|
||||
else if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "r"))) {
|
||||
else if ((f = fopen(u->sink_filename, "r"))) {
|
||||
char ln[256] = "";
|
||||
|
||||
fgets(ln, sizeof(ln)-1, f);
|
||||
|
|
@ -55,17 +70,19 @@ int pa__init(pa_module *m) {
|
|||
fclose(f);
|
||||
|
||||
if (!ln[0])
|
||||
pa_log_debug("No previous default sink setting, ignoring.");
|
||||
else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SINK, 1)) {
|
||||
pa_namereg_set_default(m->core, ln, PA_NAMEREG_SINK);
|
||||
pa_log_debug("Restored default sink '%s'.", ln);
|
||||
pa_log_info("No previous default sink setting, ignoring.");
|
||||
else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
|
||||
pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
|
||||
pa_log_info("Restored default sink '%s'.", ln);
|
||||
} else
|
||||
pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
|
||||
}
|
||||
|
||||
if (m->core->default_source_name)
|
||||
} else if (errno != ENOENT)
|
||||
pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
|
||||
|
||||
if (u->core->default_source_name)
|
||||
pa_log_info("Manually configured default source, not overwriting.");
|
||||
else if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "r"))) {
|
||||
else if ((f = fopen(u->source_filename, "r"))) {
|
||||
char ln[256] = "";
|
||||
|
||||
fgets(ln, sizeof(ln)-1, f);
|
||||
|
|
@ -73,29 +90,114 @@ int pa__init(pa_module *m) {
|
|||
fclose(f);
|
||||
|
||||
if (!ln[0])
|
||||
pa_log_debug("No previous default source setting, ignoring.");
|
||||
else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SOURCE, 1)) {
|
||||
pa_namereg_set_default(m->core, ln, PA_NAMEREG_SOURCE);
|
||||
pa_log_debug("Restored default source '%s'.", ln);
|
||||
pa_log_info("No previous default source setting, ignoring.");
|
||||
else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
|
||||
pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
|
||||
pa_log_info("Restored default source '%s'.", ln);
|
||||
} else
|
||||
pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
|
||||
|
||||
} else if (errno != ENOENT)
|
||||
pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
|
||||
}
|
||||
|
||||
static void save(struct userdata *u) {
|
||||
FILE *f;
|
||||
|
||||
if (!u->modified)
|
||||
return;
|
||||
|
||||
if (u->sink_filename) {
|
||||
if ((f = fopen(u->sink_filename, "w"))) {
|
||||
const char *n = pa_namereg_get_default_sink_name(u->core);
|
||||
fprintf(f, "%s\n", pa_strempty(n));
|
||||
fclose(f);
|
||||
} else
|
||||
pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
|
||||
}
|
||||
|
||||
if (u->source_filename) {
|
||||
if ((f = fopen(u->source_filename, "w"))) {
|
||||
const char *n = pa_namereg_get_default_source_name(u->core);
|
||||
fprintf(f, "%s\n", pa_strempty(n));
|
||||
fclose(f);
|
||||
} else
|
||||
pa_log("Failed to save default source: %s", pa_cstrerror(errno));
|
||||
}
|
||||
|
||||
u->modified = FALSE;
|
||||
}
|
||||
|
||||
static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
|
||||
struct userdata *u = userdata;
|
||||
|
||||
pa_assert(u);
|
||||
save(u);
|
||||
|
||||
if (u->time_event) {
|
||||
u->core->mainloop->time_free(u->time_event);
|
||||
u->time_event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
|
||||
struct userdata *u = userdata;
|
||||
|
||||
pa_assert(u);
|
||||
|
||||
u->modified = TRUE;
|
||||
|
||||
if (!u->time_event) {
|
||||
struct timeval tv;
|
||||
pa_gettimeofday(&tv);
|
||||
pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC);
|
||||
u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u);
|
||||
}
|
||||
}
|
||||
|
||||
int pa__init(pa_module *m) {
|
||||
struct userdata *u;
|
||||
|
||||
pa_assert(u);
|
||||
|
||||
u = pa_xnew0(struct userdata, 1);
|
||||
u->core = m->core;
|
||||
|
||||
if (!(u->sink_filename = pa_runtime_path(DEFAULT_SINK_FILE)))
|
||||
goto fail;
|
||||
|
||||
if (!(u->source_filename = pa_runtime_path(DEFAULT_SOURCE_FILE)))
|
||||
goto fail;
|
||||
|
||||
load(u);
|
||||
|
||||
u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
pa__done(m);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void pa__done(pa_module*m) {
|
||||
FILE *f;
|
||||
struct userdata *u;
|
||||
|
||||
if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "w"))) {
|
||||
const char *n = pa_namereg_get_default_sink_name(m->core);
|
||||
fprintf(f, "%s\n", n ? n : "");
|
||||
fclose(f);
|
||||
}
|
||||
pa_assert(m);
|
||||
|
||||
if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "w"))) {
|
||||
const char *n = pa_namereg_get_default_source_name(m->core);
|
||||
fprintf(f, "%s\n", n ? n : "");
|
||||
fclose(f);
|
||||
}
|
||||
if (!(u = m->userdata))
|
||||
return;
|
||||
|
||||
save(u);
|
||||
|
||||
if (u->subscription)
|
||||
pa_subscription_free(u->subscription);
|
||||
|
||||
if (u->time_event)
|
||||
m->core->mainloop->time_free(u->time_event);
|
||||
|
||||
pa_xfree(u->sink_filename);
|
||||
pa_xfree(u->source_filename);
|
||||
pa_xfree(u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
|
|||
int pa__init(pa_module*m) {
|
||||
pa_modargs *ma = NULL;
|
||||
struct userdata *u;
|
||||
char *fname, *state_dir;
|
||||
char *fname, *runtime_dir;
|
||||
char hn[256];
|
||||
pa_sink *sink;
|
||||
pa_source *source;
|
||||
|
|
@ -290,11 +290,11 @@ int pa__init(pa_module*m) {
|
|||
if (!pa_get_host_name(hn, sizeof(hn)))
|
||||
goto fail;
|
||||
|
||||
if (!(state_dir = pa_get_state_dir()))
|
||||
if (!(runtime_dir = pa_get_runtime_dir()))
|
||||
goto fail;
|
||||
|
||||
fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", state_dir, hn);
|
||||
pa_xfree(state_dir);
|
||||
fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", runtime_dir, hn);
|
||||
pa_xfree(runtime_dir);
|
||||
|
||||
if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
|
||||
pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
|
||||
|
|
@ -316,6 +316,7 @@ int pa__init(pa_module*m) {
|
|||
|
||||
fail:
|
||||
pa__done(m);
|
||||
|
||||
if (ma)
|
||||
pa_modargs_free(ma);
|
||||
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
|
||||
|
||||
case PA_SINK_SUSPENDED:
|
||||
pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
|
||||
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
|
||||
|
||||
pa_smoother_pause(u->smoother, pa_rtclock_usec());
|
||||
break;
|
||||
|
|
@ -211,7 +211,7 @@ static void thread_func(void *userdata) {
|
|||
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
|
||||
|
||||
/* Render some data and write it to the fifo */
|
||||
if (PA_SINK_OPENED(u->sink->thread_info.state) && pollfd->revents) {
|
||||
if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
|
||||
pa_usec_t usec;
|
||||
int64_t n;
|
||||
|
||||
|
|
@ -294,7 +294,7 @@ static void thread_func(void *userdata) {
|
|||
}
|
||||
|
||||
/* Hmm, nothing to do. Let's sleep */
|
||||
pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
|
||||
pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;
|
||||
}
|
||||
|
||||
if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
|
||||
|
|
|
|||
|
|
@ -102,10 +102,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
case PA_SINK_MESSAGE_GET_LATENCY: {
|
||||
pa_usec_t usec = 0;
|
||||
|
||||
/* Get the latency of the master sink */
|
||||
if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
|
||||
usec = 0;
|
||||
|
||||
*((pa_usec_t*) data) = usec /* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec) */;
|
||||
/* Add the latency internal to our sink input on top */
|
||||
usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
|
||||
|
||||
*((pa_usec_t*) data) = usec;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +124,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
|
|||
pa_sink_assert_ref(s);
|
||||
pa_assert_se(u = s->userdata);
|
||||
|
||||
if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
|
||||
if (PA_SINK_IS_LINKED(state) &&
|
||||
u->sink_input &&
|
||||
PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
|
||||
|
||||
pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
|
||||
|
||||
return 0;
|
||||
|
|
@ -134,7 +141,7 @@ static void sink_request_rewind(pa_sink *s) {
|
|||
pa_assert_se(u = s->userdata);
|
||||
|
||||
/* Just hand this one over to the master sink */
|
||||
pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE);
|
||||
pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE);
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
|
|
@ -145,24 +152,9 @@ static void sink_update_requested_latency(pa_sink *s) {
|
|||
pa_assert_se(u = s->userdata);
|
||||
|
||||
/* Just hand this one over to the master sink */
|
||||
u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s);
|
||||
pa_sink_invalidate_requested_latency(u->master);
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
|
||||
struct userdata *u = PA_SINK_INPUT(o)->userdata;
|
||||
|
||||
switch (code) {
|
||||
case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
|
||||
*((pa_usec_t*) data) = 0 /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec)*/;
|
||||
|
||||
/* Fall through, the default handler will add in the extra
|
||||
* latency added by the resampler */
|
||||
break;
|
||||
}
|
||||
|
||||
return pa_sink_input_process_msg(o, code, data, offset, chunk);
|
||||
pa_sink_input_set_requested_latency_within_thread(
|
||||
u->sink_input,
|
||||
pa_sink_get_requested_latency_within_thread(s));
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
|
|
@ -192,20 +184,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
|
|||
dst = (float*) pa_memblock_acquire(chunk->memblock);
|
||||
|
||||
for (c = 0; c < u->channels; c++) {
|
||||
unsigned j;
|
||||
float *p, *q;
|
||||
|
||||
p = src + c;
|
||||
q = u->input;
|
||||
for (j = 0; j < n; j++, p += u->channels, q++)
|
||||
*q = PA_CLAMP_UNLIKELY(*p, -1.0, 1.0);
|
||||
|
||||
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n);
|
||||
u->descriptor->run(u->handle[c], n);
|
||||
|
||||
q = u->output;
|
||||
p = dst + c;
|
||||
for (j = 0; j < n; j++, q++, p += u->channels)
|
||||
*p = PA_CLAMP_UNLIKELY(*q, -1.0, 1.0);
|
||||
pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n);
|
||||
}
|
||||
|
||||
pa_memblock_release(tchunk.memblock);
|
||||
|
|
@ -245,6 +226,9 @@ static void sink_input_detach_cb(pa_sink_input *i) {
|
|||
pa_assert_se(u = i->userdata);
|
||||
|
||||
pa_sink_detach_within_thread(u->sink);
|
||||
|
||||
pa_sink_set_asyncmsgq(u->sink, NULL);
|
||||
pa_sink_set_rtpoll(u->sink, NULL);
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
|
|
@ -648,7 +632,6 @@ int pa__init(pa_module*m) {
|
|||
if (!u->sink_input)
|
||||
goto fail;
|
||||
|
||||
u->sink_input->parent.process_msg = sink_input_process_msg;
|
||||
u->sink_input->pop = sink_input_pop_cb;
|
||||
u->sink_input->process_rewind = sink_input_process_rewind_cb;
|
||||
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
|
||||
|
|
|
|||
|
|
@ -82,12 +82,14 @@ static int load_rules(struct userdata *u, const char *filename) {
|
|||
|
||||
pa_assert(u);
|
||||
|
||||
f = filename ?
|
||||
fopen(fn = pa_xstrdup(filename), "r") :
|
||||
pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r");
|
||||
if (filename)
|
||||
f = fopen(fn = pa_xstrdup(filename), "r");
|
||||
else
|
||||
f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn);
|
||||
|
||||
if (!f) {
|
||||
pa_log("failed to open file '%s': %s", fn, pa_cstrerror(errno));
|
||||
pa_xfree(fn);
|
||||
pa_log("Failed to open file config file: %s", pa_cstrerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
/***
|
||||
This file is part of PulseAudio.
|
||||
|
||||
Copyright 2004-2006 Lennart Poettering
|
||||
Copyright 2004-2008 Lennart Poettering
|
||||
|
||||
PulseAudio is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
|
|
@ -64,6 +64,7 @@ PA_MODULE_USAGE(
|
|||
"description=<description for the sink>");
|
||||
|
||||
#define DEFAULT_SINK_NAME "null"
|
||||
#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
|
||||
|
||||
struct userdata {
|
||||
pa_core *core;
|
||||
|
|
@ -76,7 +77,8 @@ struct userdata {
|
|||
|
||||
size_t block_size;
|
||||
|
||||
struct timeval timestamp;
|
||||
pa_usec_t block_usec;
|
||||
pa_usec_t timestamp;
|
||||
};
|
||||
|
||||
static const char* const valid_modargs[] = {
|
||||
|
|
@ -96,26 +98,93 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
case PA_SINK_MESSAGE_SET_STATE:
|
||||
|
||||
if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
|
||||
pa_rtclock_get(&u->timestamp);
|
||||
u->timestamp = pa_rtclock_usec();
|
||||
|
||||
break;
|
||||
|
||||
case PA_SINK_MESSAGE_GET_LATENCY: {
|
||||
struct timeval now;
|
||||
pa_usec_t now;
|
||||
|
||||
pa_rtclock_get(&now);
|
||||
now = pa_rtclock_usec();
|
||||
*((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0;
|
||||
|
||||
if (pa_timeval_cmp(&u->timestamp, &now) > 0)
|
||||
*((pa_usec_t*) data) = 0;
|
||||
else
|
||||
*((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now);
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return pa_sink_process_msg(o, code, data, offset, chunk);
|
||||
}
|
||||
|
||||
static void sink_update_requested_latency_cb(pa_sink *s) {
|
||||
struct userdata *u;
|
||||
|
||||
pa_sink_assert_ref(s);
|
||||
u = s->userdata;
|
||||
pa_assert(u);
|
||||
|
||||
u->block_usec = pa_sink_get_requested_latency_within_thread(s);
|
||||
}
|
||||
|
||||
static void process_rewind(struct userdata *u, pa_usec_t now) {
|
||||
size_t rewind_nbytes, in_buffer;
|
||||
pa_usec_t delay;
|
||||
|
||||
pa_assert(u);
|
||||
|
||||
/* Figure out how much we shall rewind and reset the counter */
|
||||
rewind_nbytes = u->sink->thread_info.rewind_nbytes;
|
||||
u->sink->thread_info.rewind_nbytes = 0;
|
||||
|
||||
pa_assert(rewind_nbytes > 0);
|
||||
pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
|
||||
|
||||
if (u->timestamp <= now)
|
||||
return;
|
||||
|
||||
delay = u->timestamp - now;
|
||||
in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
|
||||
|
||||
if (in_buffer <= 0)
|
||||
return;
|
||||
|
||||
if (rewind_nbytes > in_buffer)
|
||||
rewind_nbytes = in_buffer;
|
||||
|
||||
pa_sink_process_rewind(u->sink, rewind_nbytes);
|
||||
u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
|
||||
|
||||
pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
|
||||
}
|
||||
|
||||
static void process_render(struct userdata *u, pa_usec_t now) {
|
||||
size_t nbytes;
|
||||
size_t ate = 0;
|
||||
|
||||
/* This is the configured latency. Sink inputs connected to us
|
||||
might not have a single frame more than this value queued. Hence:
|
||||
at maximum read this many bytes from the sink inputs. */
|
||||
|
||||
nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
|
||||
|
||||
/* Fill the buffer up the the latency size */
|
||||
while (u->timestamp < now + u->block_usec) {
|
||||
pa_memchunk chunk;
|
||||
|
||||
pa_sink_render(u->sink, nbytes, &chunk);
|
||||
pa_memblock_unref(chunk.memblock);
|
||||
|
||||
pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length);
|
||||
u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
|
||||
|
||||
ate += chunk.length;
|
||||
|
||||
if (ate >= nbytes)
|
||||
break;
|
||||
}
|
||||
|
||||
pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes);
|
||||
}
|
||||
|
||||
static void thread_func(void *userdata) {
|
||||
struct userdata *u = userdata;
|
||||
|
||||
|
|
@ -126,23 +195,24 @@ static void thread_func(void *userdata) {
|
|||
pa_thread_mq_install(&u->thread_mq);
|
||||
pa_rtpoll_install(u->rtpoll);
|
||||
|
||||
pa_rtclock_get(&u->timestamp);
|
||||
u->timestamp = pa_rtclock_usec();
|
||||
|
||||
for (;;) {
|
||||
int ret;
|
||||
|
||||
/* Render some data and drop it immediately */
|
||||
if (u->sink->thread_info.state == PA_SINK_RUNNING) {
|
||||
struct timeval now;
|
||||
pa_usec_t now;
|
||||
|
||||
pa_rtclock_get(&now);
|
||||
now = pa_rtclock_usec();
|
||||
|
||||
if (pa_timeval_cmp(&u->timestamp, &now) <= 0) {
|
||||
pa_sink_skip(u->sink, u->block_size);
|
||||
pa_timeval_add(&u->timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec));
|
||||
}
|
||||
if (u->sink->thread_info.rewind_nbytes > 0)
|
||||
process_rewind(u, now);
|
||||
|
||||
pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp);
|
||||
if (u->timestamp <= now)
|
||||
process_render(u, now);
|
||||
|
||||
pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
|
||||
} else
|
||||
pa_rtpoll_set_timer_disabled(u->rtpoll);
|
||||
|
||||
|
|
@ -197,26 +267,26 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
|
||||
pa_sink_new_data_set_sample_spec(&data, &ss);
|
||||
pa_sink_new_data_set_channel_map(&data, &map);
|
||||
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "NULL sink"));
|
||||
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output"));
|
||||
|
||||
u->sink = pa_sink_new(m->core, &data, 0);
|
||||
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
|
||||
pa_sink_new_data_done(&data);
|
||||
|
||||
if (!u->sink) {
|
||||
pa_log("Failed to create sink.");
|
||||
pa_log("Failed to create sink object.");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
u->sink->parent.process_msg = sink_process_msg;
|
||||
u->sink->update_requested_latency = sink_update_requested_latency_cb;
|
||||
u->sink->userdata = u;
|
||||
u->sink->flags = PA_SINK_LATENCY;
|
||||
|
||||
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
|
||||
pa_sink_set_rtpoll(u->sink, u->rtpoll);
|
||||
|
||||
u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */
|
||||
if (u->block_size <= 0)
|
||||
u->block_size = pa_frame_size(&ss);
|
||||
u->block_usec = u->sink->max_latency = MAX_LATENCY_USEC;
|
||||
|
||||
u->sink->thread_info.max_rewind = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
|
||||
|
||||
if (!(u->thread = pa_thread_new(thread_func, u))) {
|
||||
pa_log("Failed to create thread.");
|
||||
|
|
|
|||
|
|
@ -161,10 +161,10 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
|
|||
|
||||
pa_log_debug("trigger");
|
||||
|
||||
if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state))
|
||||
if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state))
|
||||
enable_bits |= PCM_ENABLE_INPUT;
|
||||
|
||||
if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state))
|
||||
if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
|
||||
enable_bits |= PCM_ENABLE_OUTPUT;
|
||||
|
||||
pa_log_debug("trigger: %i", enable_bits);
|
||||
|
|
@ -202,7 +202,7 @@ static void trigger(struct userdata *u, pa_bool_t quick) {
|
|||
* register the fd as ready.
|
||||
*/
|
||||
|
||||
if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) {
|
||||
if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
|
||||
uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size);
|
||||
pa_read(u->fd, buf, u->in_fragment_size, NULL);
|
||||
pa_xfree(buf);
|
||||
|
|
@ -641,7 +641,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
|
||||
|
||||
case PA_SINK_SUSPENDED:
|
||||
pa_assert(PA_SINK_OPENED(u->sink->thread_info.state));
|
||||
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
|
||||
|
||||
if (!u->source || u->source_suspended) {
|
||||
if (suspend(u) < 0)
|
||||
|
|
@ -658,7 +658,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
|
||||
if (u->sink->thread_info.state == PA_SINK_INIT) {
|
||||
do_trigger = TRUE;
|
||||
quick = u->source && PA_SOURCE_OPENED(u->source->thread_info.state);
|
||||
quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state);
|
||||
}
|
||||
|
||||
if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
|
||||
|
|
@ -721,7 +721,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
|
|||
|
||||
switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
|
||||
case PA_SOURCE_SUSPENDED:
|
||||
pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state));
|
||||
pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
|
||||
|
||||
if (!u->sink || u->sink_suspended) {
|
||||
if (suspend(u) < 0)
|
||||
|
|
@ -738,7 +738,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
|
|||
|
||||
if (u->source->thread_info.state == PA_SOURCE_INIT) {
|
||||
do_trigger = TRUE;
|
||||
quick = u->sink && PA_SINK_OPENED(u->sink->thread_info.state);
|
||||
quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state);
|
||||
}
|
||||
|
||||
if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
|
||||
|
|
@ -877,7 +877,7 @@ static void thread_func(void *userdata) {
|
|||
|
||||
/* Render some data and write it to the dsp */
|
||||
|
||||
if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
|
||||
if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) {
|
||||
|
||||
if (u->use_mmap) {
|
||||
|
||||
|
|
@ -985,7 +985,7 @@ static void thread_func(void *userdata) {
|
|||
|
||||
/* Try to read some data and pass it on to the source driver. */
|
||||
|
||||
if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
|
||||
if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) {
|
||||
|
||||
if (u->use_mmap) {
|
||||
|
||||
|
|
@ -1095,8 +1095,8 @@ static void thread_func(void *userdata) {
|
|||
|
||||
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
|
||||
pollfd->events =
|
||||
((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0) |
|
||||
((u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0);
|
||||
((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 */
|
||||
|
|
|
|||
|
|
@ -215,15 +215,6 @@ int pa__init(pa_module*m) {
|
|||
#else
|
||||
pa_socket_server *s;
|
||||
int r;
|
||||
char tmp[PATH_MAX];
|
||||
|
||||
#if defined(USE_PROTOCOL_ESOUND)
|
||||
#if defined(USE_PER_USER_ESOUND_SOCKET)
|
||||
char esdsocketpath[PATH_MAX];
|
||||
#else
|
||||
const char esdsocketpath[] = "/tmp/.esd/socket";
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
pa_assert(m);
|
||||
|
|
@ -255,27 +246,28 @@ int pa__init(pa_module*m) {
|
|||
goto fail;
|
||||
|
||||
if (s_ipv4)
|
||||
if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma)))
|
||||
pa_socket_server_unref(s_ipv4);
|
||||
|
||||
u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma);
|
||||
if (s_ipv6)
|
||||
if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma)))
|
||||
pa_socket_server_unref(s_ipv6);
|
||||
u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma);
|
||||
|
||||
if (!u->protocol_ipv4 && !u->protocol_ipv6)
|
||||
goto fail;
|
||||
|
||||
if (s_ipv6)
|
||||
pa_socket_server_unref(s_ipv6);
|
||||
if (s_ipv6)
|
||||
pa_socket_server_unref(s_ipv4);
|
||||
|
||||
#else
|
||||
|
||||
#if defined(USE_PROTOCOL_ESOUND)
|
||||
|
||||
#if defined(USE_PER_USER_ESOUND_SOCKET)
|
||||
snprintf(esdsocketpath, sizeof(esdsocketpath), "/tmp/.esd-%lu/socket", (unsigned long) getuid());
|
||||
u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid());
|
||||
#else
|
||||
u->socket_path = pa_xstrdup("/tmp/.esd/socket");
|
||||
#endif
|
||||
|
||||
pa_runtime_path(pa_modargs_get_value(ma, "socket", esdsocketpath), tmp, sizeof(tmp));
|
||||
u->socket_path = pa_xstrdup(tmp);
|
||||
|
||||
/* This socket doesn't reside in our own runtime dir but in
|
||||
* /tmp/.esd/, hence we have to create the dir first */
|
||||
|
||||
|
|
@ -285,24 +277,26 @@ int pa__init(pa_module*m) {
|
|||
}
|
||||
|
||||
#else
|
||||
pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp));
|
||||
u->socket_path = pa_xstrdup(tmp);
|
||||
#endif
|
||||
|
||||
if ((r = pa_unix_socket_remove_stale(tmp)) < 0) {
|
||||
pa_log("Failed to remove stale UNIX socket '%s': %s", tmp, pa_cstrerror(errno));
|
||||
if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) {
|
||||
pa_log("Failed to generate socket path.");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (r)
|
||||
pa_log("Removed stale UNIX socket '%s'.", tmp);
|
||||
if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) {
|
||||
pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno));
|
||||
goto fail;
|
||||
} else if (r > 0)
|
||||
pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path);
|
||||
|
||||
if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp)))
|
||||
if (!(s = pa_socket_server_new_unix(m->core->mainloop, u->socket_path)))
|
||||
goto fail;
|
||||
|
||||
if (!(u->protocol_unix = protocol_new(m->core, s, m, ma)))
|
||||
goto fail;
|
||||
|
||||
pa_socket_server_unref(s);
|
||||
|
||||
#endif
|
||||
|
||||
m->userdata = u;
|
||||
|
|
@ -325,24 +319,22 @@ fail:
|
|||
#else
|
||||
if (u->protocol_unix)
|
||||
protocol_free(u->protocol_unix);
|
||||
|
||||
if (u->socket_path)
|
||||
pa_xfree(u->socket_path);
|
||||
pa_xfree(u->socket_path);
|
||||
#endif
|
||||
|
||||
pa_xfree(u);
|
||||
} else {
|
||||
#if defined(USE_TCP_SOCKETS)
|
||||
if (s_ipv4)
|
||||
pa_socket_server_unref(s_ipv4);
|
||||
if (s_ipv6)
|
||||
pa_socket_server_unref(s_ipv6);
|
||||
#else
|
||||
if (s)
|
||||
pa_socket_server_unref(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USE_TCP_SOCKETS)
|
||||
if (s_ipv4)
|
||||
pa_socket_server_unref(s_ipv4);
|
||||
if (s_ipv6)
|
||||
pa_socket_server_unref(s_ipv6);
|
||||
#else
|
||||
if (s)
|
||||
pa_socket_server_unref(s);
|
||||
#endif
|
||||
|
||||
goto finish;
|
||||
}
|
||||
|
||||
|
|
@ -362,7 +354,7 @@ void pa__done(pa_module*m) {
|
|||
if (u->protocol_unix)
|
||||
protocol_free(u->protocol_unix);
|
||||
|
||||
#if defined(USE_PROTOCOL_ESOUND)
|
||||
#if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET)
|
||||
if (u->socket_path) {
|
||||
char *p = pa_parent_dir(u->socket_path);
|
||||
rmdir(p);
|
||||
|
|
|
|||
|
|
@ -81,10 +81,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
case PA_SINK_MESSAGE_GET_LATENCY: {
|
||||
pa_usec_t usec = 0;
|
||||
|
||||
/* Get the latency of the master sink */
|
||||
if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
|
||||
usec = 0;
|
||||
|
||||
*((pa_usec_t*) data) = usec/* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec)*/;
|
||||
/* Add the latency internal to our sink input on top */
|
||||
usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
|
||||
|
||||
*((pa_usec_t*) data) = usec;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -99,7 +103,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
|
|||
pa_sink_assert_ref(s);
|
||||
pa_assert_se(u = s->userdata);
|
||||
|
||||
if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input)))
|
||||
if (PA_SINK_IS_LINKED(state) &&
|
||||
u->sink_input &&
|
||||
PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
|
||||
|
||||
pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
|
||||
|
||||
return 0;
|
||||
|
|
@ -112,7 +119,7 @@ static void sink_request_rewind(pa_sink *s) {
|
|||
pa_sink_assert_ref(s);
|
||||
pa_assert_se(u = s->userdata);
|
||||
|
||||
pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE);
|
||||
pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE);
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
|
|
@ -123,24 +130,9 @@ static void sink_update_requested_latency(pa_sink *s) {
|
|||
pa_assert_se(u = s->userdata);
|
||||
|
||||
/* Just hand this one over to the master sink */
|
||||
u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s);
|
||||
pa_sink_invalidate_requested_latency(u->master);
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
|
||||
struct userdata *u = PA_SINK_INPUT(o)->userdata;
|
||||
|
||||
switch (code) {
|
||||
case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
|
||||
*((pa_usec_t*) data) = 0; /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);*/
|
||||
|
||||
/* Fall through, the default handler will add in the extra
|
||||
* latency added by the resampler */
|
||||
break;
|
||||
}
|
||||
|
||||
return pa_sink_input_process_msg(o, code, data, offset, chunk);
|
||||
pa_sink_input_set_requested_latency_within_thread(
|
||||
u->sink_input,
|
||||
pa_sink_get_requested_latency_within_thread(s));
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
|
|
@ -152,7 +144,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
|
|||
pa_assert_se(u = i->userdata);
|
||||
|
||||
pa_sink_render(u->sink, nbytes, chunk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -185,6 +176,9 @@ static void sink_input_detach_cb(pa_sink_input *i) {
|
|||
pa_assert_se(u = i->userdata);
|
||||
|
||||
pa_sink_detach_within_thread(u->sink);
|
||||
|
||||
pa_sink_set_asyncmsgq(u->sink, NULL);
|
||||
pa_sink_set_rtpoll(u->sink, NULL);
|
||||
}
|
||||
|
||||
/* Called from I/O thread context */
|
||||
|
|
@ -317,7 +311,6 @@ int pa__init(pa_module*m) {
|
|||
if (!u->sink_input)
|
||||
goto fail;
|
||||
|
||||
u->sink_input->parent.process_msg = sink_input_process_msg;
|
||||
u->sink_input->pop = sink_input_pop_cb;
|
||||
u->sink_input->process_rewind = sink_input_process_rewind_cb;
|
||||
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
|
|||
|
||||
if (pa_sink_used_by(s) <= 0) {
|
||||
|
||||
if (PA_SINK_OPENED(state))
|
||||
if (PA_SINK_IS_OPENED(state))
|
||||
restart(d);
|
||||
|
||||
}
|
||||
|
|
@ -328,7 +328,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
|
|||
|
||||
if (pa_source_used_by(s) <= 0) {
|
||||
|
||||
if (PA_SOURCE_OPENED(state))
|
||||
if (PA_SOURCE_IS_OPENED(state))
|
||||
restart(d);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
|
||||
/* First, change the state, because otherwide pa_sink_render() would fail */
|
||||
if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0)
|
||||
if (PA_SINK_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data)))
|
||||
if (PA_SINK_IS_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data)))
|
||||
send_data(u);
|
||||
|
||||
return r;
|
||||
|
|
@ -314,7 +314,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
pa_assert(offset > 0);
|
||||
u->requested_bytes += (size_t) offset;
|
||||
|
||||
if (PA_SINK_OPENED(u->sink->thread_info.state))
|
||||
if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
|
||||
send_data(u);
|
||||
|
||||
return 0;
|
||||
|
|
@ -343,7 +343,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
|
|||
switch ((pa_sink_state_t) state) {
|
||||
|
||||
case PA_SINK_SUSPENDED:
|
||||
pa_assert(PA_SINK_OPENED(s->state));
|
||||
pa_assert(PA_SINK_IS_OPENED(s->state));
|
||||
stream_cork(u, TRUE);
|
||||
break;
|
||||
|
||||
|
|
@ -369,7 +369,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
|
|||
switch (code) {
|
||||
case SOURCE_MESSAGE_POST:
|
||||
|
||||
if (PA_SOURCE_OPENED(u->source->thread_info.state))
|
||||
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
|
||||
pa_source_post(u->source, chunk);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -385,7 +385,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
|
|||
switch ((pa_source_state_t) state) {
|
||||
|
||||
case PA_SOURCE_SUSPENDED:
|
||||
pa_assert(PA_SOURCE_OPENED(s->state));
|
||||
pa_assert(PA_SOURCE_IS_OPENED(s->state));
|
||||
stream_cork(u, TRUE);
|
||||
break;
|
||||
|
||||
|
|
@ -1066,7 +1066,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
|
|||
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
|
||||
pa_tagstruct_puts(reply, u->sink_name);
|
||||
pa_tagstruct_putu32(reply, u->maxlength);
|
||||
pa_tagstruct_put_boolean(reply, !PA_SINK_OPENED(pa_sink_get_state(u->sink)));
|
||||
pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)));
|
||||
pa_tagstruct_putu32(reply, u->tlength);
|
||||
pa_tagstruct_putu32(reply, u->prebuf);
|
||||
pa_tagstruct_putu32(reply, u->minreq);
|
||||
|
|
@ -1082,7 +1082,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
|
|||
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
|
||||
pa_tagstruct_puts(reply, u->source_name);
|
||||
pa_tagstruct_putu32(reply, u->maxlength);
|
||||
pa_tagstruct_put_boolean(reply, !PA_SOURCE_OPENED(pa_source_get_state(u->source)));
|
||||
pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source)));
|
||||
pa_tagstruct_putu32(reply, u->fragsize);
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -134,16 +134,12 @@ static int load_rules(struct userdata *u) {
|
|||
char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
|
||||
char *ln = buf_name;
|
||||
|
||||
f = u->table_file ?
|
||||
fopen(u->table_file, "r") :
|
||||
pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r");
|
||||
|
||||
if (!f) {
|
||||
if (!(f = fopen(u->table_file, "r"))) {
|
||||
if (errno == ENOENT) {
|
||||
pa_log_info("starting with empty ruleset.");
|
||||
pa_log_info("Starting with empty ruleset.");
|
||||
ret = 0;
|
||||
} else
|
||||
pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
|
||||
pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
|
||||
|
||||
goto finish;
|
||||
}
|
||||
|
|
@ -236,11 +232,7 @@ static int save_rules(struct userdata *u) {
|
|||
|
||||
pa_log_info("Saving rules...");
|
||||
|
||||
f = u->table_file ?
|
||||
fopen(u->table_file, "w") :
|
||||
pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w");
|
||||
|
||||
if (!f) {
|
||||
if (!(f = fopen(u->table_file, "w"))) {
|
||||
pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
|
||||
goto finish;
|
||||
}
|
||||
|
|
@ -496,7 +488,7 @@ int pa__init(pa_module*m) {
|
|||
u = pa_xnew(struct userdata, 1);
|
||||
u->core = m->core;
|
||||
u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
||||
u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL));
|
||||
u->table_file = pa_runtime_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE));
|
||||
u->modified = FALSE;
|
||||
u->subscription = NULL;
|
||||
u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue