summaryrefslogtreecommitdiff
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/authkey.c2
-rw-r--r--src/pulsecore/card.c11
-rw-r--r--src/pulsecore/card.h6
-rw-r--r--src/pulsecore/cli-command.c30
-rw-r--r--src/pulsecore/cli-text.c68
-rw-r--r--src/pulsecore/core-util.c56
-rw-r--r--src/pulsecore/core-util.h1
-rw-r--r--src/pulsecore/dbus-shared.c107
-rw-r--r--src/pulsecore/dbus-shared.h42
-rw-r--r--src/pulsecore/dbus-util.c410
-rw-r--r--src/pulsecore/dbus-util.h63
-rw-r--r--src/pulsecore/hook-list.c6
-rw-r--r--src/pulsecore/hook-list.h2
-rw-r--r--src/pulsecore/log.c39
-rw-r--r--src/pulsecore/log.h4
-rw-r--r--src/pulsecore/macro.h34
-rw-r--r--src/pulsecore/memblock.c43
-rw-r--r--src/pulsecore/memblockq.c82
-rw-r--r--src/pulsecore/memblockq.h7
-rw-r--r--src/pulsecore/native-common.h6
-rw-r--r--src/pulsecore/parseaddr.c10
-rw-r--r--src/pulsecore/pdispatch.c15
-rw-r--r--src/pulsecore/proplist-util.c9
-rw-r--r--src/pulsecore/protocol-esound.c15
-rw-r--r--src/pulsecore/protocol-native.c647
-rw-r--r--src/pulsecore/refcnt.h44
-rw-r--r--src/pulsecore/rtclock.c16
-rw-r--r--src/pulsecore/sink-input.c216
-rw-r--r--src/pulsecore/sink-input.h33
-rw-r--r--src/pulsecore/sink.c376
-rw-r--r--src/pulsecore/sink.h29
-rw-r--r--src/pulsecore/socket-server.c23
-rw-r--r--src/pulsecore/source-output.c114
-rw-r--r--src/pulsecore/source-output.h14
-rw-r--r--src/pulsecore/source.c205
-rw-r--r--src/pulsecore/source.h11
-rw-r--r--src/pulsecore/time-smoother.c79
-rw-r--r--src/pulsecore/time-smoother.h14
38 files changed, 2177 insertions, 712 deletions
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index b122fee..1e31d07 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -190,7 +190,7 @@ int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
return pa_authkey_load(p, data, length);
}
-/* Store the specified cookie in the speicified cookie file */
+/* Store the specified cookie in the specified cookie file */
int pa_authkey_save(const char *fn, const void *data, size_t length) {
int fd = -1;
int unlock = 0, ret = -1;
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 6419c23..8101a92 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -141,9 +141,11 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
data->profiles = NULL;
c->active_profile = NULL;
+ c->save_profile = FALSE;
if (data->active_profile && c->profiles)
- c->active_profile = pa_hashmap_get(c->profiles, data->active_profile);
+ if ((c->active_profile = pa_hashmap_get(c->profiles, data->active_profile)))
+ c->save_profile = data->save_profile;
if (!c->active_profile && c->profiles) {
void *state = NULL;
@@ -209,7 +211,7 @@ void pa_card_free(pa_card *c) {
pa_xfree(c);
}
-int pa_card_set_profile(pa_card *c, const char *name) {
+int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) {
pa_card_profile *profile;
pa_assert(c);
@@ -224,8 +226,10 @@ int pa_card_set_profile(pa_card *c, const char *name) {
if (!(profile = pa_hashmap_get(c->profiles, name)))
return -1;
- if (c->active_profile == profile)
+ if (c->active_profile == profile) {
+ c->save_profile = c->save_profile || save;
return 0;
+ }
if (c->set_profile(c, profile) < 0)
return -1;
@@ -235,6 +239,7 @@ int pa_card_set_profile(pa_card *c, const char *name) {
pa_log_info("Changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
c->active_profile = profile;
+ c->save_profile = save;
return 0;
}
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index c80d4e2..3b7608f 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -63,6 +63,8 @@ struct pa_card {
pa_hashmap *profiles;
pa_card_profile *active_profile;
+ pa_bool_t save_profile;
+
void *userdata;
int (*set_profile)(pa_card *c, pa_card_profile *profile);
@@ -80,6 +82,8 @@ typedef struct pa_card_new_data {
char *active_profile;
pa_bool_t namereg_fail:1;
+
+ pa_bool_t save_profile:1;
} pa_card_new_data;
pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra);
@@ -93,7 +97,7 @@ void pa_card_new_data_done(pa_card_new_data *data);
pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
void pa_card_free(pa_card *c);
-int pa_card_set_profile(pa_card *c, const char *name);
+int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save);
int pa_card_suspend(pa_card *c, pa_bool_t suspend);
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index b5f7e7f..15fe525 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -175,7 +175,7 @@ static const struct command commands[] = {
{ "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3},
{ "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
{ "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
- { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (aargs: index, name)", 3},
+ { "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (args: index, name)", 3},
{ "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2},
{ "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
{ "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
@@ -524,7 +524,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
+ pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE);
return 0;
}
@@ -566,7 +566,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
}
pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
- pa_sink_input_set_volume(si, &cvolume, TRUE);
+ pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE);
return 0;
}
@@ -699,7 +699,10 @@ static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_s
return -1;
}
- p = pa_proplist_from_string(s);
+ if (!(p = pa_proplist_from_string(s))) {
+ pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+ return -1;
+ }
pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
@@ -733,7 +736,10 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa
return -1;
}
- p = pa_proplist_from_string(s);
+ if (!(p = pa_proplist_from_string(s))) {
+ pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+ return -1;
+ }
pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
@@ -773,7 +779,10 @@ static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t
return -1;
}
- p = pa_proplist_from_string(s);
+ if (!(p = pa_proplist_from_string(s))) {
+ pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+ return -1;
+ }
pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
@@ -813,7 +822,10 @@ static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer
return -1;
}
- p = pa_proplist_from_string(s);
+ if (!(p = pa_proplist_from_string(s))) {
+ pa_strbuf_puts(buf, "Failed to parse proplist.\n");
+ return -1;
+ }
pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
@@ -1454,7 +1466,7 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return -1;
}
- if (pa_card_set_profile(card, p) < 0) {
+ if (pa_card_set_profile(card, p, TRUE) < 0) {
pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
return -1;
}
@@ -1504,7 +1516,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
nl = 1;
}
- pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)));
+ pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE, TRUE)));
pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));
pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));
}
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 76adc4d..604678b 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -220,19 +220,17 @@ char *pa_sink_list_to_string(pa_core *c) {
v[PA_VOLUME_SNPRINT_MAX],
vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
- pa_usec_t min_latency, max_latency;
const char *cmn;
cmn = pa_channel_map_to_pretty_name(&sink->channel_map);
- pa_sink_get_latency_range(sink, &min_latency, &max_latency);
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
@@ -240,7 +238,6 @@ char *pa_sink_list_to_string(pa_core *c) {
"\tvolume steps: %u\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
- "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
"\tmax request: %lu KiB\n"
"\tmax rewind: %lu KiB\n"
"\tmonitor source: %u\n"
@@ -258,21 +255,19 @@ char *pa_sink_list_to_string(pa_core *c) {
sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
- sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME" : "",
+ sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME " : "",
+ sink->flags & PA_SINK_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
sink_state_to_string(pa_sink_get_state(sink)),
- pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
+ pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE, FALSE)),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
- sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
- pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),
+ sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE, FALSE)) : "",
+ pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE, FALSE), &sink->channel_map),
pa_volume_snprint(v, sizeof(v), sink->base_volume),
sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "",
sink->n_volume_steps,
pa_yes_no(pa_sink_get_mute(sink, FALSE)),
(double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC,
- (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
- (double) min_latency / PA_USEC_PER_MSEC,
- (double) max_latency / PA_USEC_PER_MSEC,
(unsigned long) pa_sink_get_max_request(sink) / 1024,
(unsigned long) pa_sink_get_max_rewind(sink) / 1024,
sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
@@ -283,6 +278,22 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_sink_used_by(sink),
pa_sink_linked_by(sink));
+ if (sink->flags & PA_SINK_DYNAMIC_LATENCY) {
+ pa_usec_t min_latency, max_latency;
+ pa_sink_get_latency_range(sink, &min_latency, &max_latency);
+
+ pa_strbuf_printf(
+ s,
+ "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n",
+ (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
+ (double) min_latency / PA_USEC_PER_MSEC,
+ (double) max_latency / PA_USEC_PER_MSEC);
+ } else
+ pa_strbuf_printf(
+ s,
+ "\tfixed latency: %0.2f ms\n",
+ (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC);
+
if (sink->card)
pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
if (sink->module)
@@ -313,19 +324,16 @@ char *pa_source_list_to_string(pa_core *c) {
v[PA_VOLUME_SNPRINT_MAX],
vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
- pa_usec_t min_latency, max_latency;
const char *cmn;
cmn = pa_channel_map_to_pretty_name(&source->channel_map);
- pa_source_get_latency_range(source, &min_latency, &max_latency);
-
pa_strbuf_printf(
s,
" %c index: %u\n"
"\tname: <%s>\n"
"\tdriver: <%s>\n"
- "\tflags: %s%s%s%s%s%s\n"
+ "\tflags: %s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tvolume: %s%s%s\n"
"\t balance %0.2f\n"
@@ -333,7 +341,6 @@ char *pa_source_list_to_string(pa_core *c) {
"\tvolume steps: %u\n"
"\tmuted: %s\n"
"\tcurrent latency: %0.2f ms\n"
- "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
"\tmax rewind: %lu KiB\n"
"\tsample spec: %s\n"
"\tchannel map: %s%s%s\n"
@@ -349,6 +356,7 @@ char *pa_source_list_to_string(pa_core *c) {
source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
+ source->flags & PA_SOURCE_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "",
source_state_to_string(pa_source_get_state(source)),
pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)),
source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
@@ -360,9 +368,6 @@ char *pa_source_list_to_string(pa_core *c) {
source->n_volume_steps,
pa_yes_no(pa_source_get_mute(source, FALSE)),
(double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
- (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
- (double) min_latency / PA_USEC_PER_MSEC,
- (double) max_latency / PA_USEC_PER_MSEC,
(unsigned long) pa_source_get_max_rewind(source) / 1024,
pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
@@ -371,6 +376,22 @@ char *pa_source_list_to_string(pa_core *c) {
pa_source_used_by(source),
pa_source_linked_by(source));
+ if (source->flags & PA_SOURCE_DYNAMIC_LATENCY) {
+ pa_usec_t min_latency, max_latency;
+ pa_source_get_latency_range(source, &min_latency, &max_latency);
+
+ pa_strbuf_printf(
+ s,
+ "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n",
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
+ (double) min_latency / PA_USEC_PER_MSEC,
+ (double) max_latency / PA_USEC_PER_MSEC);
+ } else
+ pa_strbuf_printf(
+ s,
+ "\tfixed latency: %0.2f ms\n",
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC);
+
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
if (source->card)
@@ -486,6 +507,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {
char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
pa_usec_t cl;
const char *cmn;
+ pa_cvolume v;
+
+ pa_sink_input_get_volume(i, &v, TRUE);
cmn = pa_channel_map_to_pretty_name(&i->channel_map);
@@ -526,9 +550,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
- pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
- pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_input_get_volume(i)),
- pa_cvolume_get_balance(pa_sink_input_get_volume(i), &i->channel_map),
+ pa_cvolume_snprint(cv, sizeof(cv), &v),
+ pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &v),
+ pa_cvolume_get_balance(&v, &i->channel_map),
pa_yes_no(pa_sink_input_get_mute(i)),
(double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
clt,
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 0d243ee..24d929d 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -326,7 +326,7 @@ ssize_t pa_write(int fd, const void *buf, size_t count, int *type) {
}
/** Calls read() in a loop. Makes sure that as much as 'size' bytes,
- * unless EOF is reached or an error occured */
+ * unless EOF is reached or an error occurred */
ssize_t pa_loop_read(int fd, void*data, size_t size, int *type) {
ssize_t ret = 0;
int _type;
@@ -1242,7 +1242,7 @@ int pa_lock_lockfile(const char *fn) {
goto fail;
}
- /* Check wheter the file has been removed meanwhile. When yes,
+ /* Check whether the file has been removed meanwhile. When yes,
* restart this loop, otherwise, we're done */
if (st.st_nlink >= 1)
break;
@@ -2462,7 +2462,7 @@ char *pa_machine_id(void) {
pa_strip_nl(ln);
if (r && ln[0])
- return pa_xstrdup(ln);
+ return pa_utf8_filter(ln);
}
/* The we fall back to the host name. It supposed to be somewhat
@@ -2480,13 +2480,16 @@ char *pa_machine_id(void) {
break;
} else if (strlen(c) < l-1) {
+ char *u;
if (*c == 0) {
pa_xfree(c);
break;
}
- return c;
+ u = pa_utf8_filter(c);
+ pa_xfree(c);
+ return u;
}
/* Hmm, the hostname is as long the space we offered the
@@ -2498,10 +2501,19 @@ char *pa_machine_id(void) {
}
/* If no hostname was set we use the POSIX hostid. It's usually
- * the IPv4 address. Mit not be that stable. */
+ * the IPv4 address. Might not be that stable. */
return pa_sprintf_malloc("%08lx", (unsigned long) gethostid);
}
+char *pa_session_id(void) {
+ const char *e;
+
+ if (!(e = getenv("XDG_SESSION_COOKIE")))
+ return NULL;
+
+ return pa_utf8_filter(e);
+}
+
char *pa_uname_string(void) {
struct utsname u;
@@ -2608,7 +2620,7 @@ char *pa_unescape(char *p) {
}
char *pa_realpath(const char *path) {
- char *r, *t;
+ char *t;
pa_assert(path);
/* We want only abolsute paths */
@@ -2617,17 +2629,31 @@ char *pa_realpath(const char *path) {
return NULL;
}
-#ifndef __GLIBC__
-#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
-#endif
+#if defined(__GLIBC__) || defined(__APPLE__)
+ {
+ char *r;
- if (!(r = realpath(path, NULL)))
- return NULL;
+ if (!(r = realpath(path, NULL)))
+ return NULL;
+
+ /* We copy this here in case our pa_xmalloc() is not
+ * implemented on top of libc malloc() */
+ t = pa_xstrdup(r);
+ pa_xfree(r);
+ }
+#elif defined(PATH_MAX)
+ {
+ char *path_buf;
+ path_buf = pa_xmalloc(PATH_MAX);
- /* We copy this here in case our pa_xmalloc() is not implemented
- * on top of libc malloc() */
- t = pa_xstrdup(r);
- pa_xfree(r);
+ if (!(t = realpath(path, path_buf))) {
+ pa_xfree(path_buf);
+ return NULL;
+ }
+ }
+#else
+#error "It's not clear whether this system supports realpath(..., NULL) like GNU libc does. If it doesn't we need a private version of realpath() here."
+#endif
return t;
}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 0ba33f3..f96fa44 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -202,6 +202,7 @@ pa_bool_t pa_in_system_mode(void);
#define pa_streq(a,b) (!strcmp((a),(b)))
char *pa_machine_id(void);
+char *pa_session_id(void);
char *pa_uname_string(void);
#ifdef HAVE_VALGRIND_MEMCHECK_H
diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c
new file mode 100644
index 0000000..9d9445b
--- /dev/null
+++ b/src/pulsecore/dbus-shared.c
@@ -0,0 +1,107 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006, 2009 Lennart Poettering
+ Copyright 2006 Shams E. King
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+#include <pulsecore/log.h>
+#include <pulsecore/shared.h>
+
+#include "dbus-shared.h"
+
+struct pa_dbus_connection {
+ PA_REFCNT_DECLARE;
+
+ pa_dbus_wrap_connection *connection;
+ pa_core *core;
+ const char *property_name;
+};
+
+static pa_dbus_connection* dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) {
+ pa_dbus_connection *pconn;
+
+ pconn = pa_xnew(pa_dbus_connection, 1);
+ PA_REFCNT_INIT(pconn);
+ pconn->core = c;
+ pconn->property_name = name;
+ pconn->connection = conn;
+
+ pa_shared_set(c, name, pconn);
+
+ return pconn;
+}
+
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
+
+ static const char *const prop_name[] = {
+ [DBUS_BUS_SESSION] = "dbus-connection-session",
+ [DBUS_BUS_SYSTEM] = "dbus-connection-system",
+ [DBUS_BUS_STARTER] = "dbus-connection-starter"
+ };
+ pa_dbus_wrap_connection *conn;
+ pa_dbus_connection *pconn;
+
+ pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
+
+ if ((pconn = pa_shared_get(c, prop_name[type])))
+ return pa_dbus_connection_ref(pconn);
+
+ if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, type, error)))
+ return NULL;
+
+ return dbus_connection_new(c, conn, prop_name[type]);
+}
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
+ pa_assert(c->connection);
+
+ return pa_dbus_wrap_connection_get(c->connection);
+}
+
+void pa_dbus_connection_unref(pa_dbus_connection *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
+
+ if (PA_REFCNT_DEC(c) > 0)
+ return;
+
+ pa_dbus_wrap_connection_free(c->connection);
+
+ pa_shared_remove(c->core, c->property_name);
+ pa_xfree(c);
+}
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) > 0);
+
+ PA_REFCNT_INC(c);
+
+ return c;
+}
diff --git a/src/pulsecore/dbus-shared.h b/src/pulsecore/dbus-shared.h
new file mode 100644
index 0000000..4c15455
--- /dev/null
+++ b/src/pulsecore/dbus-shared.h
@@ -0,0 +1,42 @@
+#ifndef foodbussharedhfoo
+#define foodbussharedhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006, 2009 Lennart Poettering
+ Copyright 2006 Shams E. King
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/dbus-util.h>
+
+typedef struct pa_dbus_connection pa_dbus_connection;
+
+/* return a pa_dbus_connection of the specified type for the given core,
+ * like dbus_bus_get(), but integrates the connection with the pa_core */
+pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error);
+
+DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn);
+
+pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn);
+void pa_dbus_connection_unref(pa_dbus_connection *conn);
+
+#endif
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
new file mode 100644
index 0000000..ece36de
--- /dev/null
+++ b/src/pulsecore/dbus-util.c
@@ -0,0 +1,410 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Lennart Poettering
+ Copyright 2006 Shams E. King
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdarg.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "dbus-util.h"
+
+struct pa_dbus_wrap_connection {
+ pa_mainloop_api *mainloop;
+ DBusConnection *connection;
+ pa_defer_event* dispatch_event;
+};
+
+static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
+ DBusConnection *conn = userdata;
+
+ if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
+ /* no more data to process, disable the deferred */
+ ea->defer_enable(ev, 0);
+ }
+}
+
+/* DBusDispatchStatusFunction callback for the pa mainloop */
+static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
+ pa_dbus_wrap_connection *c = userdata;
+
+ pa_assert(c);
+
+ switch(status) {
+
+ case DBUS_DISPATCH_COMPLETE:
+ c->mainloop->defer_enable(c->dispatch_event, 0);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ c->mainloop->defer_enable(c->dispatch_event, 1);
+ break;
+ }
+}
+
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+ unsigned int flags;
+ pa_io_event_flags_t events = 0;
+
+ pa_assert(watch);
+
+ flags = dbus_watch_get_flags(watch);
+
+ /* no watch flags for disabled watches */
+ if (!dbus_watch_get_enabled(watch))
+ return PA_IO_EVENT_NULL;
+
+ if (flags & DBUS_WATCH_READABLE)
+ events |= PA_IO_EVENT_INPUT;
+ if (flags & DBUS_WATCH_WRITABLE)
+ events |= PA_IO_EVENT_OUTPUT;
+
+ return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+}
+
+/* pa_io_event_cb_t IO event handler */
+static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+ unsigned int flags = 0;
+ DBusWatch *watch = userdata;
+
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+ pa_assert(fd == dbus_watch_get_unix_fd(watch));
+#else
+ pa_assert(fd == dbus_watch_get_fd(watch));
+#endif
+
+ if (!dbus_watch_get_enabled(watch)) {
+ pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
+ return;
+ }
+
+ if (events & PA_IO_EVENT_INPUT)
+ flags |= DBUS_WATCH_READABLE;
+ if (events & PA_IO_EVENT_OUTPUT)
+ flags |= DBUS_WATCH_WRITABLE;
+ if (events & PA_IO_EVENT_HANGUP)
+ flags |= DBUS_WATCH_HANGUP;
+ if (events & PA_IO_EVENT_ERROR)
+ flags |= DBUS_WATCH_ERROR;
+
+ dbus_watch_handle(watch, flags);
+}
+
+/* pa_time_event_cb_t timer event handler */
+static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ DBusTimeout *timeout = userdata;
+
+ if (dbus_timeout_get_enabled(timeout)) {
+ struct timeval next = *tv;
+ dbus_timeout_handle(timeout);
+
+ /* restart it for the next scheduled time */
+ pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+ ea->time_restart(e, &next);
+ }
+}
+
+/* DBusAddWatchFunction callback for pa mainloop */
+static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(c);
+
+ ev = c->mainloop->io_new(
+ c->mainloop,
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+ dbus_watch_get_unix_fd(watch),
+#else
+ dbus_watch_get_fd(watch),
+#endif
+ get_watch_flags(watch), handle_io_event, watch);
+
+ dbus_watch_set_data(watch, ev, NULL);
+
+ return TRUE;
+}
+
+/* DBusRemoveWatchFunction callback for pa mainloop */
+static void remove_watch(DBusWatch *watch, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(c);
+
+ if ((ev = dbus_watch_get_data(watch)))
+ c->mainloop->io_free(ev);
+}
+
+/* DBusWatchToggledFunction callback for pa mainloop */
+static void toggle_watch(DBusWatch *watch, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_io_event *ev;
+
+ pa_assert(watch);
+ pa_assert(c);
+
+ pa_assert_se(ev = dbus_watch_get_data(watch));
+
+ /* get_watch_flags() checks if the watch is enabled */
+ c->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+/* DBusAddTimeoutFunction callback for pa mainloop */
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_time_event *ev;
+ struct timeval tv;
+
+ pa_assert(timeout);
+ pa_assert(c);
+
+ if (!dbus_timeout_get_enabled(timeout))
+ return FALSE;
+
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+ ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout);
+
+ dbus_timeout_set_data(timeout, ev, NULL);
+
+ return TRUE;
+}
+
+/* DBusRemoveTimeoutFunction callback for pa mainloop */
+static void remove_timeout(DBusTimeout *timeout, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_time_event *ev;
+
+ pa_assert(timeout);
+ pa_assert(c);
+
+ if ((ev = dbus_timeout_get_data(timeout)))
+ c->mainloop->time_free(ev);
+}
+
+/* DBusTimeoutToggledFunction callback for pa mainloop */
+static void toggle_timeout(DBusTimeout *timeout, void *data) {
+ pa_dbus_wrap_connection *c = data;
+ pa_time_event *ev;
+
+ pa_assert(timeout);
+ pa_assert(c);
+
+ pa_assert_se(ev = dbus_timeout_get_data(timeout));
+
+ if (dbus_timeout_get_enabled(timeout)) {
+ struct timeval tv;
+
+ pa_gettimeofday(&tv);
+ pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+ c->mainloop->time_restart(ev, &tv);
+ } else
+ c->mainloop->time_restart(ev, NULL);
+}
+
+static void wakeup_main(void *userdata) {
+ pa_dbus_wrap_connection *c = userdata;
+
+ pa_assert(c);
+
+ /* this will wakeup the mainloop and dispatch events, although
+ * it may not be the cleanest way of accomplishing it */
+ c->mainloop->defer_enable(c->dispatch_event, 1);
+}
+
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBusType type, DBusError *error) {
+ DBusConnection *conn;
+ pa_dbus_wrap_connection *pconn;
+ char *id;
+
+ pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
+
+ if (!(conn = dbus_bus_get_private(type, error)))
+ return NULL;
+
+ pconn = pa_xnew(pa_dbus_wrap_connection, 1);
+ pconn->mainloop = m;
+ pconn->connection = conn;
+
+ dbus_connection_set_exit_on_disconnect(conn, FALSE);
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
+ dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
+ dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
+ dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
+
+ pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
+
+ pa_log_debug("Successfully connected to D-Bus %s bus %s as %s",
+ type == DBUS_BUS_SYSTEM ? "system" : (type == DBUS_BUS_SESSION ? "session" : "starter"),
+ pa_strnull((id = dbus_connection_get_server_id(conn))),
+ pa_strnull(dbus_bus_get_unique_name(conn)));
+
+ dbus_free(id);
+
+ return pconn;
+}
+
+void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
+ pa_assert(c);
+
+ if (dbus_connection_get_is_connected(c->connection)) {
+ dbus_connection_close(c->connection);
+ /* must process remaining messages, bit of a kludge to handle
+ * both unload and shutdown */
+ while (dbus_connection_read_write_dispatch(c->connection, -1))
+ ;
+ }
+
+ c->mainloop->defer_free(c->dispatch_event);
+ dbus_connection_unref(c->connection);
+ pa_xfree(c);
+}
+
+DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
+ pa_assert(c);
+ pa_assert(c->connection);
+
+ return c->connection;
+}
+
+int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
+ const char *t;
+ va_list ap;
+ unsigned k = 0;
+
+ pa_assert(c);
+ pa_assert(error);
+
+ va_start(ap, error);
+ while ((t = va_arg(ap, const char*))) {
+ dbus_bus_add_match(c, t, error);
+
+ if (dbus_error_is_set(error))
+ goto fail;
+
+ k++;
+ }
+ va_end(ap);
+ return 0;
+
+fail:
+
+ va_end(ap);
+ va_start(ap, error);
+ for (; k > 0; k--) {
+ DBusError e;
+
+ pa_assert_se(t = va_arg(ap, const char*));
+
+ dbus_error_init(&e);
+ dbus_bus_remove_match(c, t, &e);
+ dbus_error_free(&e);
+ }
+ va_end(ap);
+
+ return -1;
+}
+
+void pa_dbus_remove_matches(DBusConnection *c, ...) {
+ const char *t;
+ va_list ap;
+ DBusError error;
+
+ pa_assert(c);
+
+ dbus_error_init(&error);
+
+ va_start(ap, c);
+ while ((t = va_arg(ap, const char*))) {
+ dbus_bus_remove_match(c, t, &error);
+ dbus_error_free(&error);
+ }
+ va_end(ap);
+}
+
+pa_dbus_pending *pa_dbus_pending_new(
+ DBusConnection *c,
+ DBusMessage *m,
+ DBusPendingCall *pending,
+ void *context_data,
+ void *call_data) {
+
+ pa_dbus_pending *p;
+
+ pa_assert(pending);
+
+ p = pa_xnew(pa_dbus_pending, 1);
+ p->connection = c;
+ p->message = m;
+ p->pending = pending;
+ p->context_data = context_data;
+ p->call_data = call_data;
+
+ PA_LLIST_INIT(pa_dbus_pending, p);
+
+ return p;
+}
+
+void pa_dbus_pending_free(pa_dbus_pending *p) {
+ pa_assert(p);
+
+ if (p->pending) {
+ dbus_pending_call_cancel(p->pending);
+ dbus_pending_call_unref(p->pending);
+ }
+
+ if (p->message)
+ dbus_message_unref(p->message);
+
+ pa_xfree(p);
+}
+
+void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
+ pa_assert(p);
+
+ while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
+ ;
+}
+
+void pa_dbus_free_pending_list(pa_dbus_pending **p) {
+ pa_dbus_pending *i;
+
+ pa_assert(p);
+
+ while ((i = *p)) {
+ PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
+ pa_dbus_pending_free(i);
+ }
+}
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
new file mode 100644
index 0000000..55cda7a
--- /dev/null
+++ b/src/pulsecore/dbus-util.h
@@ -0,0 +1,63 @@
+#ifndef foodbusutilhfoo
+#define foodbusutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006 Shams E. King
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/llist.h>
+#include <pulse/mainloop-api.h>
+
+/* A wrap connection is not shared or refcounted, it is available in client side */
+typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
+
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error);
+void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
+
+DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);
+
+int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) PA_GCC_SENTINEL;
+void pa_dbus_remove_matches(DBusConnection *c, ...) PA_GCC_SENTINEL;
+
+typedef struct pa_dbus_pending pa_dbus_pending;
+
+struct pa_dbus_pending {
+ DBusConnection *connection;
+ DBusMessage *message;
+ DBusPendingCall *pending;
+
+ void *context_data;
+ void *call_data;
+
+ PA_LLIST_FIELDS(pa_dbus_pending);
+};
+
+pa_dbus_pending *pa_dbus_pending_new(DBusConnection *c, DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data);
+void pa_dbus_pending_free(pa_dbus_pending *p);
+
+/* Sync up a list of pa_dbus_pending_call objects */
+void pa_dbus_sync_pending_list(pa_dbus_pending **p);
+
+/* Free up a list of pa_dbus_pending_call objects */
+void pa_dbus_free_pending_list(pa_dbus_pending **p);
+
+#endif
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index 5f7a866..a00116d 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -121,3 +121,9 @@ pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data) {
return result;
}
+
+pa_bool_t pa_hook_is_firing(pa_hook *hook) {
+ pa_assert(hook);
+
+ return hook->n_firing > 0;
+}
diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h
index 8514cce..86ce9d2 100644
--- a/src/pulsecore/hook-list.h
+++ b/src/pulsecore/hook-list.h
@@ -71,4 +71,6 @@ void pa_hook_slot_free(pa_hook_slot *slot);
pa_hook_result_t pa_hook_fire(pa_hook *hook, void *data);
+pa_bool_t pa_hook_is_firing(pa_hook *hook);
+
#endif
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index d4d3b76..60ea9c5 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -59,12 +59,13 @@
#define ENV_LOG_PRINT_META "PULSE_LOG_META"
#define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
#define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
+#define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
static char *ident = NULL; /* in local charset format */
static pa_log_target_t target = PA_LOG_STDERR, target_override;
static pa_bool_t target_override_set = FALSE;
static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
-static unsigned show_backtrace = 0, show_backtrace_override = 0;
+static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0;
static pa_log_flags_t flags = 0, flags_override = 0;
#ifdef HAVE_SYSLOG_H
@@ -128,13 +129,17 @@ void pa_log_set_show_backtrace(unsigned nlevels) {
show_backtrace = nlevels;
}
+void pa_log_set_skip_backtrace(unsigned nlevels) {
+ skip_backtrace = nlevels;
+}
+
#ifdef HAVE_EXECINFO_H
static char* get_backtrace(unsigned show_nframes) {
void* trace[32];
int n_frames;
char **symbols, *e, *r;
- unsigned j, n;
+ unsigned j, n, s;
size_t a;
pa_assert(show_nframes > 0);
@@ -149,14 +154,15 @@ static char* get_backtrace(unsigned show_nframes) {
if (!symbols)
return NULL;
- n = PA_MIN((unsigned) n_frames, show_nframes);
+ s = skip_backtrace;
+ n = PA_MIN((unsigned) n_frames, s + show_nframes);
a = 4;
- for (j = 0; j < n; j++) {
- if (j > 0)
+ for (j = s; j < n; j++) {
+ if (j > s)
a += 2;
- a += strlen(symbols[j]);
+ a += strlen(pa_path_get_filename(symbols[j]));
}
r = pa_xnew(char, a);
@@ -164,14 +170,18 @@ static char* get_backtrace(unsigned show_nframes) {
strcpy(r, " (");
e = r + 2;
- for (j = 0; j < n; j++) {
- if (j > 0) {
+ for (j = s; j < n; j++) {
+ const char *sym;
+
+ if (j > s) {
strcpy(e, "<<");
e += 2;
}
- strcpy(e, symbols[j]);
- e += strlen(symbols[j]);
+ sym = pa_path_get_filename(symbols[j]);
+
+ strcpy(e, sym);
+ e += strlen(sym);
}
strcpy(e, ")");
@@ -225,6 +235,13 @@ static void init_defaults(void) {
if (show_backtrace_override <= 0)
show_backtrace_override = 0;
}
+
+ if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
+ skip_backtrace = (unsigned) atoi(e);
+
+ if (skip_backtrace <= 0)
+ skip_backtrace = 0;
+ }
}
void pa_log_levelv_meta(
@@ -245,7 +262,7 @@ void pa_log_levelv_meta(
/* We don't use dynamic memory allocation here to minimize the hit
* in RT threads */
- char text[4096], location[128], timestamp[32];
+ char text[16*1024], location[128], timestamp[32];
pa_assert(level < PA_LOG_LEVEL_MAX);
pa_assert(format);
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 153e11e..2f379f6 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -77,6 +77,9 @@ void pa_log_set_flags(pa_log_flags_t flags, pa_log_merge_t merge);
/* Enable backtrace */
void pa_log_set_show_backtrace(unsigned nlevels);
+/* Skip the first backtrace frames */
+void pa_log_set_skip_backtrace(unsigned nlevels);
+
void pa_log_level_meta(
pa_log_level_t level,
const char*file,
@@ -110,6 +113,7 @@ void pa_log_levelv(
#define pa_log_notice(...) pa_log_level_meta(PA_LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define pa_log_warn(...) pa_log_level_meta(PA_LOG_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define pa_log_error(...) pa_log_level_meta(PA_LOG_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_logl(level, ...) pa_log_level_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
#else
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index 20015bf..a5ca696 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -164,8 +164,8 @@ typedef int pa_bool_t;
#define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL)
-/* An assert which guarantees side effects of x, i.e. is never
- * optimized away */
+/* pa_assert_se() is an assert which guarantees side effects of x,
+ * i.e. is never optimized away, regardless of NDEBUG or FASTPATH. */
#define pa_assert_se(expr) \
do { \
if (PA_UNLIKELY(!(expr))) { \
@@ -174,18 +174,44 @@ typedef int pa_bool_t;
} \
} while (FALSE)
-/* An assert that may be optimized away by defining NDEBUG */
+/* Does exactly nothing */
+#define pa_nop() do {} while (FALSE)
+
+/* pa_assert() is an assert that may be optimized away by defining
+ * NDEBUG. pa_assert_fp() is an assert that may be optimized away by
+ * defining FASTPATH. It is supposed to be used in inner loops. It's
+ * there for extra paranoia checking and should probably be removed in
+ * production builds. */
#ifdef NDEBUG
-#define pa_assert(expr) do {} while (FALSE)
+#define pa_assert(expr) pa_nop()
+#define pa_assert_fp(expr) pa_nop()
+#elif defined (FASTPATH)
+#define pa_assert(expr) pa_assert_se(expr)
+#define pa_assert_fp(expr) pa_nop()
#else
#define pa_assert(expr) pa_assert_se(expr)
+#define pa_assert_fp(expr) pa_assert_se(expr)
#endif
+#ifdef NDEBUG
+#define pa_assert_not_reached() pa_nop()
+#else
#define pa_assert_not_reached() \
do { \
pa_log_error("Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, PA_PRETTY_FUNCTION); \
abort(); \
} while (FALSE)
+#endif
+
+/* A compile time assertion */
+#define pa_assert_cc(expr) \
+ do { \
+ switch (0) { \
+ case 0: \
+ case !!(expr): \
+ ; \
+ } \
+ } while (FALSE)
#define PA_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
#define PA_UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index fbf0a47..6cc0ff3 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -745,8 +745,47 @@ void pa_mempool_free(pa_mempool *p) {
pa_flist_free(p->free_slots, NULL);
if (pa_atomic_load(&p->stat.n_allocated) > 0) {
-/* raise(SIGTRAP); */
- pa_log_warn("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+
+ /* Ouch, somebody is retaining a memory block reference! */
+
+#ifdef DEBUG_REF
+ unsigned i;
+ pa_flist *list;
+
+ /* Let's try to find at least one of those leaked memory blocks */
+
+ list = pa_flist_new(p->n_blocks);
+
+ for (i = 0; i < (unsigned) pa_atomic_load(&p->n_init); i++) {
+ struct mempool_slot *slot;
+ pa_memblock *b, *k;
+
+ slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) i));
+ b = mempool_slot_data(slot);
+
+ while ((k = pa_flist_pop(p->free_slots))) {
+ while (pa_flist_push(list, k) < 0)
+ ;
+
+ if (b == k)
+ break;
+ }
+
+ if (!k)
+ pa_log("REF: Leaked memory block %p", b);
+
+ while ((k = pa_flist_pop(list)))
+ while (pa_flist_push(p->free_slots, k) < 0)
+ ;
+ }
+
+ pa_flist_free(list, NULL);
+
+#endif
+
+ pa_log_error("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+
+/* PA_DEBUG_TRAP; */
}
pa_shm_free(&p->memory);
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index e2be42b..77f9efc 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -90,8 +90,8 @@ pa_memblockq* pa_memblockq_new(
pa_memblockq_set_maxlength(bq, maxlength);
pa_memblockq_set_tlength(bq, tlength);
- pa_memblockq_set_prebuf(bq, prebuf);
pa_memblockq_set_minreq(bq, minreq);
+ pa_memblockq_set_prebuf(bq, prebuf);
pa_memblockq_set_maxrewind(bq, maxrewind);
pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
@@ -601,7 +601,7 @@ size_t pa_memblockq_missing(pa_memblockq *bq) {
return l >= bq->minreq ? l : 0;
}
-void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, pa_bool_t account) {
int64_t old, delta;
pa_assert(bq);
@@ -628,12 +628,14 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) {
delta = bq->write_index - old;
- if (delta >= (int64_t) bq->requested) {
- delta -= (int64_t) bq->requested;
- bq->requested = 0;
- } else if (delta >= 0) {
- bq->requested -= (size_t) delta;
- delta = 0;
+ if (account) {
+ if (delta >= (int64_t) bq->requested) {
+ delta -= (int64_t) bq->requested;
+ bq->requested = 0;
+ } else if (delta >= 0) {
+ bq->requested -= (size_t) delta;
+ delta = 0;
+ }
}
bq->missing -= delta;
@@ -782,16 +784,13 @@ void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
if (bq->tlength > bq->maxlength)
pa_memblockq_set_tlength(bq, bq->maxlength);
-
- if (bq->prebuf > bq->maxlength)
- pa_memblockq_set_prebuf(bq, bq->maxlength);
}
void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
size_t old_tlength;
pa_assert(bq);
- if (tlength <= 0)
+ if (tlength <= 0 || tlength == (size_t) -1)
tlength = bq->maxlength;
old_tlength = bq->tlength;
@@ -800,55 +799,72 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
if (bq->tlength > bq->maxlength)
bq->tlength = bq->maxlength;
- if (bq->prebuf > bq->tlength)
- pa_memblockq_set_prebuf(bq, bq->tlength);
-
if (bq->minreq > bq->tlength)
pa_memblockq_set_minreq(bq, bq->tlength);
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+
bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
}
+void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+ pa_assert(bq);
+
+ bq->minreq = (minreq/bq->base)*bq->base;
+
+ if (bq->minreq > bq->tlength)
+ bq->minreq = bq->tlength;
+
+ if (bq->minreq < bq->base)
+ bq->minreq = bq->base;
+
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+}
+
void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
pa_assert(bq);
if (prebuf == (size_t) -1)
- prebuf = bq->tlength;
+ prebuf = bq->tlength+bq->base-bq->minreq;
bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
if (prebuf > 0 && bq->prebuf < bq->base)
bq->prebuf = bq->base;
- if (bq->prebuf > bq->tlength)
- bq->prebuf = bq->tlength;
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ bq->prebuf = bq->tlength+bq->base-bq->minreq;
if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
bq->in_prebuf = FALSE;
-
- if (bq->minreq > bq->prebuf)
- pa_memblockq_set_minreq(bq, bq->prebuf);
}
-void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
pa_assert(bq);
- bq->minreq = (minreq/bq->base)*bq->base;
-
- if (bq->minreq > bq->tlength)
- bq->minreq = bq->tlength;
+ bq->maxrewind = (maxrewind/bq->base)*bq->base;
+}
- if (bq->minreq > bq->prebuf)
- bq->minreq = bq->prebuf;
+void pa_memblockq_apply_attr(pa_memblockq *bq, const pa_buffer_attr *a) {
+ pa_assert(bq);
+ pa_assert(a);
- if (bq->minreq < bq->base)
- bq->minreq = bq->base;
+ pa_memblockq_set_maxlength(bq, a->maxlength);
+ pa_memblockq_set_tlength(bq, a->tlength);
+ pa_memblockq_set_prebuf(bq, a->prebuf);
+ pa_memblockq_set_minreq(bq, a->minreq);
}
-void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
+void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a) {
pa_assert(bq);
+ pa_assert(a);
- bq->maxrewind = (maxrewind/bq->base)*bq->base;
+ a->maxlength = (uint32_t) pa_memblockq_get_maxlength(bq);
+ a->tlength = (uint32_t) pa_memblockq_get_tlength(bq);
+ a->prebuf = (uint32_t) pa_memblockq_get_prebuf(bq);
+ a->minreq = (uint32_t) pa_memblockq_get_minreq(bq);
}
int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
@@ -875,7 +891,7 @@ int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) {
pa_memblock_unref(chunk.memblock);
} else
- pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE);
+ pa_memblockq_seek(bq, (int64_t) chunk.length, PA_SEEK_RELATIVE, TRUE);
pa_memblockq_drop(bq, chunk.length);
}
diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h
index 0a74aa3..146d261 100644
--- a/src/pulsecore/memblockq.h
+++ b/src/pulsecore/memblockq.h
@@ -85,7 +85,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *chunk);
int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk);
/* Manipulate the write pointer */
-void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek);
+void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek, pa_bool_t account);
/* Return a copy of the next memory chunk in the queue. It is not
* removed from the queue. There are two reasons this function might
@@ -158,6 +158,10 @@ void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq);
void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t maxrewind); /* Set the maximum history size */
void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence);
+/* Apply the data from pa_buffer_attr */
+void pa_memblockq_apply_attr(pa_memblockq *memblockq, const pa_buffer_attr *a);
+void pa_memblockq_get_attr(pa_memblockq *bq, pa_buffer_attr *a);
+
/* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */
void pa_memblockq_willneed(pa_memblockq *bq);
@@ -175,5 +179,4 @@ pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq);
/* Return how many items are currently stored in the queue */
unsigned pa_memblockq_get_nblocks(pa_memblockq *bq);
-
#endif
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 6951e10..d4d7f3e 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -152,7 +152,7 @@ enum {
/* Supported since protocol v14 (0.9.12) */
PA_COMMAND_EXTENSION,
- /* Supported since protocol v15 (0.9.15*/
+ /* Supported since protocol v15 (0.9.15) */
PA_COMMAND_GET_CARD_INFO,
PA_COMMAND_GET_CARD_INFO_LIST,
PA_COMMAND_SET_CARD_PROFILE,
@@ -161,6 +161,10 @@ enum {
PA_COMMAND_PLAYBACK_STREAM_EVENT,
PA_COMMAND_RECORD_STREAM_EVENT,
+ /* SERVER->CLIENT */
+ PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED,
+ PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index c5cd7fe..5b53122 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -87,13 +87,15 @@ int pa_parse_address(const char *name, pa_parsed_address *ret_p) {
ret_p->type = PA_PARSED_ADDRESS_TCP_AUTO;
if (*name == '{') {
- char hn[256], *pfx;
- /* The URL starts with a host specification for detecting local connections */
+ char *id, *pfx;
- if (!pa_get_host_name(hn, sizeof(hn)))
+ /* The URL starts with a host id for detecting local connections */
+ if (!(id = pa_machine_id()))
return -1;
- pfx = pa_sprintf_malloc("{%s}", hn);
+ pfx = pa_sprintf_malloc("{%s}", id);
+ pa_xfree(id);
+
if (!pa_startswith(name, pfx)) {
pa_xfree(pfx);
/* Not local */
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 305941a..d00106b 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -165,7 +165,20 @@ static const char *command_names[PA_COMMAND_MAX] = {
[PA_COMMAND_STARTED] = "STARTED",
/* Supported since protocol v14 (0.9.12) */
- [PA_COMMAND_EXTENSION] = "EXTENSION"
+ [PA_COMMAND_EXTENSION] = "EXTENSION",
+
+
+ [PA_COMMAND_GET_CARD_INFO] = "GET_CARD_INFO",
+ [PA_COMMAND_GET_CARD_INFO_LIST] = "GET_CARD_INFO_LIST",
+ [PA_COMMAND_SET_CARD_PROFILE] = "SET_CARD_PROFILE",
+
+ [PA_COMMAND_CLIENT_EVENT] = "GET_CLIENT_EVENT",
+ [PA_COMMAND_PLAYBACK_STREAM_EVENT] = "PLAYBACK_STREAM_EVENT",
+ [PA_COMMAND_RECORD_STREAM_EVENT] = "RECORD_STREAM_EVENT",
+
+ /* SERVER->CLIENT */
+ [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = "PLAYBACK_BUFFER_ATTR_CHANGED",
+ [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = "RECORD_BUFFER_ATTR_CHANGED"
};
#endif
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index af5f0aa..eac8927 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -231,12 +231,11 @@ void pa_init_proplist(pa_proplist *p) {
}
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_SESSION_ID)) {
- const char *t;
+ char *s;
- if ((t = getenv("XDG_SESSION_COOKIE"))) {
- char *c = pa_utf8_filter(t);
- pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_SESSION_ID, c);
- pa_xfree(c);
+ if ((s = pa_session_id())) {
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_SESSION_ID, s);
+ pa_xfree(s);
}
}
}
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 2b80c65..7e7126e 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -562,7 +562,7 @@ static int esd_proto_get_latency(connection *c, esd_proto_t request, const void
pa_sink *sink;
int32_t latency;
- connection_ref(c);
+ connection_assert_ref(c);
pa_assert(!data);
pa_assert(length == 0);
@@ -575,6 +575,7 @@ static int esd_proto_get_latency(connection *c, esd_proto_t request, const void
latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
connection_write(c, &latency, sizeof(int32_t));
+
return 0;
}
@@ -583,7 +584,7 @@ static int esd_proto_server_info(connection *c, esd_proto_t request, const void
int32_t response;
pa_sink *sink;
- connection_ref(c);
+ connection_assert_ref(c);
pa_assert(data);
pa_assert(length == sizeof(int32_t));
@@ -611,7 +612,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
unsigned nsamples;
char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
- connection_ref(c);
+ connection_assert_ref(c);
pa_assert(data);
pa_assert(length == sizeof(int32_t));
@@ -637,7 +638,8 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da
pa_assert(t >= k*2+s);
if (conn->sink_input) {
- pa_cvolume volume = *pa_sink_input_get_volume(conn->sink_input);
+ pa_cvolume volume;
+ pa_sink_input_get_volume(conn->sink_input, &volume, TRUE);
rate = (int32_t) conn->sink_input->sample_spec.rate;
lvolume = (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
rvolume = (int32_t) ((volume.values[volume.channels == 2 ? 1 : 0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
@@ -777,7 +779,7 @@ static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *
volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
volume.channels = conn->sink_input->sample_spec.channels;
- pa_sink_input_set_volume(conn->sink_input, &volume, TRUE);
+ pa_sink_input_set_volume(conn->sink_input, &volume, TRUE, TRUE);
ok = 1;
} else
ok = 0;
@@ -1105,8 +1107,7 @@ static int do_read(connection *c) {
pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
pa_memblock_unref(c->scache.memchunk.memblock);
- c->scache.memchunk.memblock = NULL;
- c->scache.memchunk.index = c->scache.memchunk.length = 0;
+ pa_memchunk_reset(&c->scache.memchunk);
pa_xfree(c->scache.name);
c->scache.name = NULL;
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 50a9191..aecaf71 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -81,8 +81,20 @@ typedef struct record_stream {
pa_source_output *source_output;
pa_memblockq *memblockq;
- size_t fragment_size;
- pa_usec_t source_latency;
+
+ pa_bool_t adjust_latency:1;
+ pa_bool_t early_requests:1;
+
+ pa_buffer_attr buffer_attr;
+
+ pa_atomic_t on_the_fly;
+ pa_usec_t configured_source_latency;
+ size_t drop_initial;
+
+ /* Only updated after SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY */
+ size_t on_the_fly_snapshot;
+ pa_usec_t current_monitor_latency;
+ pa_usec_t current_source_latency;
} record_stream;
PA_DECLARE_CLASS(record_stream);
@@ -105,18 +117,24 @@ typedef struct playback_stream {
pa_sink_input *sink_input;
pa_memblockq *memblockq;
+
+ pa_bool_t adjust_latency:1;
+ pa_bool_t early_requests:1;
+
pa_bool_t is_underrun:1;
pa_bool_t drain_request:1;
uint32_t drain_tag;
uint32_t syncid;
pa_atomic_t missing;
- size_t minreq;
- pa_usec_t sink_latency;
+ pa_usec_t configured_sink_latency;
+ pa_buffer_attr buffer_attr;
/* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */
int64_t read_index, write_index;
size_t render_memblockq_length;
+ pa_usec_t current_sink_latency;
+ uint64_t playing_for, underrun_for;
} playback_stream;
PA_DECLARE_CLASS(playback_stream);
@@ -174,13 +192,18 @@ struct pa_native_protocol {
};
enum {
+ SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY = PA_SOURCE_OUTPUT_MESSAGE_MAX
+};
+
+enum {
SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
SINK_INPUT_MESSAGE_DRAIN, /* disabled prebuf, get playback started. */
SINK_INPUT_MESSAGE_FLUSH,
SINK_INPUT_MESSAGE_TRIGGER,
SINK_INPUT_MESSAGE_SEEK,
SINK_INPUT_MESSAGE_PREBUF_FORCE,
- SINK_INPUT_MESSAGE_UPDATE_LATENCY
+ SINK_INPUT_MESSAGE_UPDATE_LATENCY,
+ SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR
};
enum {
@@ -188,7 +211,8 @@ enum {
PLAYBACK_STREAM_MESSAGE_UNDERFLOW,
PLAYBACK_STREAM_MESSAGE_OVERFLOW,
PLAYBACK_STREAM_MESSAGE_DRAIN_ACK,
- PLAYBACK_STREAM_MESSAGE_STARTED
+ PLAYBACK_STREAM_MESSAGE_STARTED,
+ PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH
};
enum {
@@ -203,7 +227,7 @@ enum {
static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
static void sink_input_kill_cb(pa_sink_input *i);
static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend);
-static void sink_input_moved_cb(pa_sink_input *i);
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest);
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
@@ -215,11 +239,12 @@ static void playback_stream_request_bytes(struct playback_stream*s);
static void source_output_kill_cb(pa_source_output *o);
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
-static void source_output_moved_cb(pa_source_output *o);
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest);
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
+static int source_output_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -360,6 +385,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
/* structure management */
+/* Called from main context */
static void upload_stream_unlink(upload_stream *s) {
pa_assert(s);
@@ -371,6 +397,7 @@ static void upload_stream_unlink(upload_stream *s) {
upload_stream_unref(s);
}
+/* Called from main context */
static void upload_stream_free(pa_object *o) {
upload_stream *s = UPLOAD_STREAM(o);
pa_assert(s);
@@ -388,6 +415,7 @@ static void upload_stream_free(pa_object *o) {
pa_xfree(s);
}
+/* Called from main context */
static upload_stream* upload_stream_new(
pa_native_connection *c,
const pa_sample_spec *ss,
@@ -420,6 +448,7 @@ static upload_stream* upload_stream_new(
return s;
}
+/* Called from main context */
static void record_stream_unlink(record_stream *s) {
pa_assert(s);
@@ -437,6 +466,7 @@ static void record_stream_unlink(record_stream *s) {
record_stream_unref(s);
}
+/* Called from main context */
static void record_stream_free(pa_object *o) {
record_stream *s = RECORD_STREAM(o);
pa_assert(s);
@@ -447,6 +477,7 @@ static void record_stream_free(pa_object *o) {
pa_xfree(s);
}
+/* Called from main context */
static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
record_stream *s = RECORD_STREAM(o);
record_stream_assert_ref(s);
@@ -458,6 +489,10 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
case RECORD_STREAM_MESSAGE_POST_DATA:
+ /* We try to keep up to date with how many bytes are
+ * currently on the fly */
+ pa_atomic_sub(&s->on_the_fly, chunk->length);
+
if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
/* pa_log_warn("Failed to push data into output queue."); */
return -1;
@@ -472,35 +507,34 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
return 0;
}
-static void fix_record_buffer_attr_pre(
- record_stream *s,
- pa_bool_t adjust_latency,
- pa_bool_t early_requests,
- uint32_t *maxlength,
- uint32_t *fragsize) {
+/* Called from main context */
+static void fix_record_buffer_attr_pre(record_stream *s) {
size_t frame_size;
pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;
pa_assert(s);
- pa_assert(maxlength);
- pa_assert(fragsize);
+
+ /* This function will be called from the main thread, before as
+ * well as after the source output has been activated using
+ * pa_source_output_put()! That means it may not touch any
+ * ->thread_info data! */
frame_size = pa_frame_size(&s->source_output->sample_spec);
- if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
- *maxlength = MAX_MEMBLOCKQ_LENGTH;
- if (*maxlength <= 0)
- *maxlength = (uint32_t) frame_size;
+ if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
+ s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (s->buffer_attr.maxlength <= 0)
+ s->buffer_attr.maxlength = (uint32_t) frame_size;
- if (*fragsize == (uint32_t) -1)
- *fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
- if (*fragsize <= 0)
- *fragsize = (uint32_t) frame_size;
+ if (s->buffer_attr.fragsize == (uint32_t) -1)
+ s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
+ if (s->buffer_attr.fragsize <= 0)
+ s->buffer_attr.fragsize = (uint32_t) frame_size;
- orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec);
+ orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(s->buffer_attr.fragsize, &s->source_output->sample_spec);
- if (early_requests) {
+ if (s->early_requests) {
/* In early request mode we need to emulate the classic
* fragment-based playback model. We do this setting the source
@@ -508,7 +542,7 @@ static void fix_record_buffer_attr_pre(
source_usec = fragsize_usec;
- } else if (adjust_latency) {
+ } else if (s->adjust_latency) {
/* So, the user asked us to adjust the latency according to
* what the source can provide. Half the latency will be
@@ -522,73 +556,69 @@ static void fix_record_buffer_attr_pre(
/* Ok, the user didn't ask us to adjust the latency, hence we
* don't */
- source_usec = 0;
+ source_usec = (pa_usec_t) -1;
}
- if (source_usec > 0)
- s->source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
+ if (source_usec != (pa_usec_t) -1)
+ s->configured_source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
else
- s->source_latency = 0;
+ s->configured_source_latency = 0;
- if (early_requests) {
+ if (s->early_requests) {
/* Ok, we didn't necessarily get what we were asking for, so
* let's tell the user */
- fragsize_usec = s->source_latency;
+ fragsize_usec = s->configured_source_latency;
- } else if (adjust_latency) {
+ } else if (s->adjust_latency) {
/* Now subtract what we actually got */
- if (fragsize_usec >= s->source_latency*2)
- fragsize_usec -= s->source_latency;
+ if (fragsize_usec >= s->configured_source_latency*2)
+ fragsize_usec -= s->configured_source_latency;
else
- fragsize_usec = s->source_latency;
+ fragsize_usec = s->configured_source_latency;
}
if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec))
- *fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
+ s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
- if (*fragsize <= 0)
- *fragsize = (uint32_t) frame_size;
+ if (s->buffer_attr.fragsize <= 0)
+ s->buffer_attr.fragsize = (uint32_t) frame_size;
}
-static void fix_record_buffer_attr_post(
- record_stream *s,
- uint32_t *maxlength,
- uint32_t *fragsize) {
-
+/* Called from main context */
+static void fix_record_buffer_attr_post(record_stream *s) {
size_t base;
pa_assert(s);
- pa_assert(maxlength);
- pa_assert(fragsize);
- *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
+ /* This function will be called from the main thread, before as
+ * well as after the source output has been activated using
+ * pa_source_output_put()! That means it may not touch and
+ * ->thread_info data! */
base = pa_frame_size(&s->source_output->sample_spec);
- s->fragment_size = (*fragsize/base)*base;
- if (s->fragment_size <= 0)
- s->fragment_size = base;
+ s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base;
+ if (s->buffer_attr.fragsize <= 0)
+ s->buffer_attr.fragsize = base;
- if (s->fragment_size > *maxlength)
- s->fragment_size = *maxlength;
-
- *fragsize = (uint32_t) s->fragment_size;
+ if (s->buffer_attr.fragsize > s->buffer_attr.maxlength)
+ s->buffer_attr.fragsize = s->buffer_attr.maxlength;
}
+/* Called from main context */
static record_stream* record_stream_new(
pa_native_connection *c,
pa_source *source,
pa_sample_spec *ss,
pa_channel_map *map,
pa_bool_t peak_detect,
- uint32_t *maxlength,
- uint32_t *fragsize,
+ pa_buffer_attr *attr,
pa_source_output_flags_t flags,
pa_proplist *p,
pa_bool_t adjust_latency,
@@ -603,7 +633,6 @@ static record_stream* record_stream_new(
pa_assert(c);
pa_assert(ss);
- pa_assert(maxlength);
pa_assert(p);
pa_assert(ret);
@@ -632,20 +661,25 @@ static record_stream* record_stream_new(
s->parent.process_msg = record_stream_process_msg;
s->connection = c;
s->source_output = source_output;
+ s->buffer_attr = *attr;
+ s->adjust_latency = adjust_latency;
+ s->early_requests = early_requests;
+ pa_atomic_store(&s->on_the_fly, 0);
+ s->source_output->parent.process_msg = source_output_process_msg;
s->source_output->push = source_output_push_cb;
s->source_output->kill = source_output_kill_cb;
s->source_output->get_latency = source_output_get_latency_cb;
- s->source_output->moved = source_output_moved_cb;
+ s->source_output->moving = source_output_moving_cb;
s->source_output->suspend = source_output_suspend_cb;
s->source_output->send_event = source_output_send_event_cb;
s->source_output->userdata = s;
- fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);
+ fix_record_buffer_attr_pre(s);
s->memblockq = pa_memblockq_new(
0,
- *maxlength,
+ s->buffer_attr.maxlength,
0,
base = pa_frame_size(&source_output->sample_spec),
1,
@@ -653,7 +687,8 @@ static record_stream* record_stream_new(
0,
NULL);
- fix_record_buffer_attr_post(s, maxlength, fragsize);
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+ fix_record_buffer_attr_post(s);
*ss = s->source_output->sample_spec;
*map = s->source_output->channel_map;
@@ -661,14 +696,15 @@ static record_stream* record_stream_new(
pa_idxset_put(c->record_streams, s, &s->index);
pa_log_info("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
- ((double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) + (double) s->source_latency) / PA_USEC_PER_MSEC,
- (double) pa_bytes_to_usec(s->fragment_size, &source_output->sample_spec) / PA_USEC_PER_MSEC,
- (double) s->source_latency / PA_USEC_PER_MSEC);
+ ((double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->configured_source_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->configured_source_latency / PA_USEC_PER_MSEC);
pa_source_output_put(s->source_output);
return s;
}
+/* Called from main context */
static void record_stream_send_killed(record_stream *r) {
pa_tagstruct *t;
record_stream_assert_ref(r);
@@ -680,6 +716,7 @@ static void record_stream_send_killed(record_stream *r) {
pa_pstream_send_tagstruct(r->connection->pstream, t);
}
+/* Called from main context */
static void playback_stream_unlink(playback_stream *s) {
pa_assert(s);
@@ -700,6 +737,7 @@ static void playback_stream_unlink(playback_stream *s) {
playback_stream_unref(s);
}
+/* Called from main context */
static void playback_stream_free(pa_object* o) {
playback_stream *s = PLAYBACK_STREAM(o);
pa_assert(s);
@@ -710,6 +748,7 @@ static void playback_stream_free(pa_object* o) {
pa_xfree(s);
}
+/* Called from main context */
static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
playback_stream *s = PLAYBACK_STREAM(o);
playback_stream_assert_ref(s);
@@ -720,24 +759,21 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
switch (code) {
case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
pa_tagstruct *t;
- uint32_t l = 0;
+ int l = 0;
for (;;) {
- if ((l = (uint32_t) pa_atomic_load(&s->missing)) <= 0)
- break;
+ if ((l = pa_atomic_load(&s->missing)) <= 0)
+ return 0;
- if (pa_atomic_cmpxchg(&s->missing, (int) l, 0))
+ if (pa_atomic_cmpxchg(&s->missing, l, 0))
break;
}
- if (l <= 0)
- break;
-
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, l);
+ pa_tagstruct_putu32(t, (uint32_t) l);
pa_pstream_send_tagstruct(s->connection->pstream, t);
/* pa_log("Requesting %lu bytes", (unsigned long) l); */
@@ -775,7 +811,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
if (s->connection->version >= 13) {
pa_tagstruct *t;
- /* Notify the user we're overflowed*/
+ /* Notify the user we started playback */
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
@@ -788,67 +824,79 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
break;
+
+ case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH: {
+ pa_tagstruct *t;
+
+ s->buffer_attr.tlength = (uint32_t) offset;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.tlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
+ pa_tagstruct_putu32(t, s->buffer_attr.minreq);
+ pa_tagstruct_put_usec(t, s->configured_sink_latency);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+
+ break;
+ }
}
return 0;
}
-static void fix_playback_buffer_attr_pre(
- playback_stream *s,
- pa_bool_t adjust_latency,
- pa_bool_t early_requests,
- uint32_t *maxlength,
- uint32_t *tlength,
- uint32_t* prebuf,
- uint32_t* minreq) {
-
- size_t frame_size;
+/* Called from main context */
+static void fix_playback_buffer_attr(playback_stream *s) {
+ size_t frame_size, max_prebuf;
pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
pa_assert(s);
- pa_assert(maxlength);
- pa_assert(tlength);
- pa_assert(prebuf);
- pa_assert(minreq);
+
+ /* This function will be called from the main thread, before as
+ * well as after the sink input has been activated using
+ * pa_sink_input_put()! That means it may not touch any
+ * ->thread_info data, such as the memblockq! */
frame_size = pa_frame_size(&s->sink_input->sample_spec);
- if (*maxlength == (uint32_t) -1 || *maxlength > MAX_MEMBLOCKQ_LENGTH)
- *maxlength = MAX_MEMBLOCKQ_LENGTH;
- if (*maxlength <= 0)
- *maxlength = (uint32_t) frame_size;
+ if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
+ s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
+ if (s->buffer_attr.maxlength <= 0)
+ s->buffer_attr.maxlength = (uint32_t) frame_size;
- if (*tlength == (uint32_t) -1)
- *tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
- if (*tlength <= 0)
- *tlength = (uint32_t) frame_size;
+ if (s->buffer_attr.tlength == (uint32_t) -1)
+ s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (s->buffer_attr.tlength <= 0)
+ s->buffer_attr.tlength = (uint32_t) frame_size;
- if (*minreq == (uint32_t) -1)
- *minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
- if (*minreq <= 0)
- *minreq = (uint32_t) frame_size;
+ if (s->buffer_attr.minreq == (uint32_t) -1)
+ s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
+ if (s->buffer_attr.minreq <= 0)
+ s->buffer_attr.minreq = (uint32_t) frame_size;
- if (*tlength < *minreq+frame_size)
- *tlength = *minreq+(uint32_t) frame_size;
+ if (s->buffer_attr.tlength < s->buffer_attr.minreq+frame_size)
+ s->buffer_attr.tlength = s->buffer_attr.minreq+(uint32_t) frame_size;
- orig_tlength_usec = tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec);
- orig_minreq_usec = minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec);
+ orig_tlength_usec = tlength_usec = pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec);
+ orig_minreq_usec = minreq_usec = pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec);
pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
(double) tlength_usec / PA_USEC_PER_MSEC,
(double) minreq_usec / PA_USEC_PER_MSEC);
- if (early_requests) {
+ if (s->early_requests) {
/* In early request mode we need to emulate the classic
* fragment-based playback model. We do this setting the sink
* latency to the fragment size. */
sink_usec = minreq_usec;
-
pa_log_debug("Early requests mode enabled, configuring sink latency to minreq.");
- } else if (adjust_latency) {
+ } else if (s->adjust_latency) {
/* So, the user asked us to adjust the latency of the stream
* buffer according to the what the sink can provide. The
@@ -888,80 +936,60 @@ static void fix_playback_buffer_attr_pre(
pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
}
- s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
+ s->configured_sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
- if (early_requests) {
+ if (s->early_requests) {
/* Ok, we didn't necessarily get what we were asking for, so
* let's tell the user */
- minreq_usec = s->sink_latency;
+ minreq_usec = s->configured_sink_latency;
- } else if (adjust_latency) {
+ } else if (s->adjust_latency) {
/* Ok, we didn't necessarily get what we were asking for, so
* let's subtract from what we asked for for the remaining
* buffer space */
- if (tlength_usec >= s->sink_latency)
- tlength_usec -= s->sink_latency;
+ if (tlength_usec >= s->configured_sink_latency)
+ tlength_usec -= s->configured_sink_latency;
}
/* FIXME: This is actually larger than necessary, since not all of
* the sink latency is actually rewritable. */
- if (tlength_usec < s->sink_latency + 2*minreq_usec)
- tlength_usec = s->sink_latency + 2*minreq_usec;
+ if (tlength_usec < s->configured_sink_latency + 2*minreq_usec)
+ tlength_usec = s->configured_sink_latency + 2*minreq_usec;
if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
- *tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
+ s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=
pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec))
- *minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
+ s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
- if (*minreq <= 0) {
- *minreq = (uint32_t) frame_size;
- *tlength += (uint32_t) frame_size*2;
+ if (s->buffer_attr.minreq <= 0) {
+ s->buffer_attr.minreq = (uint32_t) frame_size;
+ s->buffer_attr.tlength += (uint32_t) frame_size*2;
}
- if (*tlength <= *minreq)
- *tlength = *minreq*2 + (uint32_t) frame_size;
-
- if (*prebuf == (uint32_t) -1 || *prebuf > *tlength)
- *prebuf = *tlength;
-}
-
-static void fix_playback_buffer_attr_post(
- playback_stream *s,
- uint32_t *maxlength,
- uint32_t *tlength,
- uint32_t* prebuf,
- uint32_t* minreq) {
-
- pa_assert(s);
- pa_assert(maxlength);
- pa_assert(tlength);
- pa_assert(prebuf);
- pa_assert(minreq);
+ if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
+ s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
- *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
- *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
- *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
- *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
+ max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
- s->minreq = *minreq;
+ if (s->buffer_attr.prebuf == (uint32_t) -1 ||
+ s->buffer_attr.prebuf > max_prebuf)
+ s->buffer_attr.prebuf = max_prebuf;
}
+/* Called from main context */
static playback_stream* playback_stream_new(
pa_native_connection *c,
pa_sink *sink,
pa_sample_spec *ss,
pa_channel_map *map,
- uint32_t *maxlength,
- uint32_t *tlength,
- uint32_t *prebuf,
- uint32_t *minreq,
+ pa_buffer_attr *a,
pa_cvolume *volume,
pa_bool_t muted,
pa_bool_t muted_set,
@@ -982,10 +1010,6 @@ static playback_stream* playback_stream_new(
pa_assert(c);
pa_assert(ss);
- pa_assert(maxlength);
- pa_assert(tlength);
- pa_assert(prebuf);
- pa_assert(minreq);
pa_assert(missing);
pa_assert(p);
pa_assert(ret);
@@ -1042,6 +1066,9 @@ static playback_stream* playback_stream_new(
s->is_underrun = TRUE;
s->drain_request = FALSE;
pa_atomic_store(&s->missing, 0);
+ s->buffer_attr = *a;
+ s->adjust_latency = adjust_latency;
+ s->early_requests = early_requests;
s->sink_input->parent.process_msg = sink_input_process_msg;
s->sink_input->pop = sink_input_pop_cb;
@@ -1049,28 +1076,28 @@ static playback_stream* playback_stream_new(
s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
s->sink_input->update_max_request = sink_input_update_max_request_cb;
s->sink_input->kill = sink_input_kill_cb;
- s->sink_input->moved = sink_input_moved_cb;
+ s->sink_input->moving = sink_input_moving_cb;
s->sink_input->suspend = sink_input_suspend_cb;
s->sink_input->send_event = sink_input_send_event_cb;
s->sink_input->userdata = s;
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
- fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, tlength, prebuf, minreq);
- pa_sink_input_get_silence(sink_input, &silence);
+ fix_playback_buffer_attr(s);
+ pa_sink_input_get_silence(sink_input, &silence);
s->memblockq = pa_memblockq_new(
start_index,
- *maxlength,
- *tlength,
+ s->buffer_attr.maxlength,
+ s->buffer_attr.tlength,
pa_frame_size(&sink_input->sample_spec),
- *prebuf,
- *minreq,
+ s->buffer_attr.prebuf,
+ s->buffer_attr.minreq,
0,
&silence);
-
pa_memblock_unref(silence.memblock);
- fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq);
+
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
*missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
@@ -1080,18 +1107,19 @@ static playback_stream* playback_stream_new(
pa_idxset_put(c->output_streams, s, &s->index);
pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
- ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC,
- (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
- (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
- (double) s->sink_latency / PA_USEC_PER_MSEC);
+ ((double) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->configured_sink_latency) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
+ (double) s->configured_sink_latency / PA_USEC_PER_MSEC);
pa_sink_input_put(s->sink_input);
return s;
}
-/* Called from thread context */
+/* Called from IO context */
static void playback_stream_request_bytes(playback_stream *s) {
- size_t m, previous_missing;
+ size_t m, minreq;
+ int previous_missing;
playback_stream_assert_ref(s);
@@ -1102,14 +1130,16 @@ static void playback_stream_request_bytes(playback_stream *s) {
/* pa_log("request_bytes(%lu)", (unsigned long) m); */
- previous_missing = (size_t) pa_atomic_add(&s->missing, (int) m);
+ previous_missing = pa_atomic_add(&s->missing, (int) m);
+ minreq = pa_memblockq_get_minreq(s->memblockq);
if (pa_memblockq_prebuf_active(s->memblockq) ||
- (previous_missing < s->minreq && previous_missing+m >= s->minreq))
+ (previous_missing < (int) minreq && previous_missing + (int) m >= (int) minreq))
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
}
+/* Called from main context */
static void playback_stream_send_killed(playback_stream *p) {
pa_tagstruct *t;
playback_stream_assert_ref(p);
@@ -1121,6 +1151,7 @@ static void playback_stream_send_killed(playback_stream *p) {
pa_pstream_send_tagstruct(p->connection->pstream, t);
}
+/* Called from main context */
static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
pa_native_connection *c = PA_NATIVE_CONNECTION(o);
pa_native_connection_assert_ref(c);
@@ -1142,6 +1173,7 @@ static int native_connection_process_msg(pa_msgobject *o, int code, void*userdat
return 0;
}
+/* Called from main context */
static void native_connection_unlink(pa_native_connection *c) {
record_stream *r;
output_stream *o;
@@ -1181,6 +1213,7 @@ static void native_connection_unlink(pa_native_connection *c) {
pa_native_connection_unref(c);
}
+/* Called from main context */
static void native_connection_free(pa_object *o) {
pa_native_connection *c = PA_NATIVE_CONNECTION(o);
@@ -1198,6 +1231,7 @@ static void native_connection_free(pa_object *o) {
pa_xfree(c);
}
+/* Called from main context */
static void native_connection_send_memblock(pa_native_connection *c) {
uint32_t start;
record_stream *r;
@@ -1217,8 +1251,8 @@ static void native_connection_send_memblock(pa_native_connection *c) {
if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) {
pa_memchunk schunk = chunk;
- if (schunk.length > r->fragment_size)
- schunk.length = r->fragment_size;
+ if (schunk.length > r->buffer_attr.fragsize)
+ schunk.length = r->buffer_attr.fragsize;
pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
@@ -1232,6 +1266,7 @@ static void native_connection_send_memblock(pa_native_connection *c) {
/*** sink input callbacks ***/
+/* Called from thread context */
static void handle_seek(playback_stream *s, int64_t indexw) {
playback_stream_assert_ref(s);
@@ -1284,7 +1319,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
int64_t windex;
windex = pa_memblockq_get_write_index(s->memblockq);
- pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata));
+
+ /* The client side is incapable of accounting correctly
+ * for seeks of a type != PA_SEEK_RELATIVE. We need to be
+ * able to deal with that. */
+
+ pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE);
handle_seek(s, windex);
return 0;
@@ -1302,7 +1342,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
pa_log_warn("Failed to push data into queue");
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
- pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);
+ pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
}
handle_seek(s, windex);
@@ -1371,10 +1411,14 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
}
case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
-
+ /* Atomically get a snapshot of all timing parameters... */
s->read_index = pa_memblockq_get_read_index(s->memblockq);
s->write_index = pa_memblockq_get_write_index(s->memblockq);
s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
+ s->current_sink_latency = pa_sink_get_latency_within_thread(s->sink_input->sink);
+ s->underrun_for = s->sink_input->thread_info.underrun_for;
+ s->playing_for = s->sink_input->thread_info.playing_for;
+
return 0;
case PA_SINK_INPUT_MESSAGE_SET_STATE: {
@@ -1399,6 +1443,12 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
* latency added by the resampler */
break;
}
+
+ case SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR: {
+ pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+ return 0;
+ }
}
return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
@@ -1418,7 +1468,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
if (pa_memblockq_is_readable(s->memblockq))
s->is_underrun = FALSE;
else {
-/* pa_log("%s, UNDERRUN: %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq)); */
+ if (!s->is_underrun)
+ pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
s->drain_request = FALSE;
@@ -1447,6 +1498,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
return 0;
}
+/* Called from thread context */
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
playback_stream *s;
@@ -1461,6 +1513,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_memblockq_rewind(s->memblockq, nbytes);
}
+/* Called from thread context */
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
playback_stream *s;
@@ -1471,18 +1524,30 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_memblockq_set_maxrewind(s->memblockq, nbytes);
}
+/* Called from thread context */
static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
playback_stream *s;
- size_t tlength;
+ size_t new_tlength, old_tlength;
pa_sink_input_assert_ref(i);
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
- tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
+ old_tlength = pa_memblockq_get_tlength(s->memblockq);
+ new_tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
+
+ if (old_tlength < new_tlength) {
+ pa_log_debug("max_request changed, trying to update from %zu to %zu.", old_tlength, new_tlength);
+ pa_memblockq_set_tlength(s->memblockq, new_tlength);
+ new_tlength = pa_memblockq_get_tlength(s->memblockq);
- if (pa_memblockq_get_tlength(s->memblockq) < tlength)
- pa_memblockq_set_tlength(s->memblockq, tlength);
+ if (new_tlength == old_tlength)
+ pa_log_debug("Failed to increase tlength");
+ else {
+ pa_log_debug("Notifying client about increased tlength");
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, NULL, pa_memblockq_get_tlength(s->memblockq), NULL, NULL);
+ }
+ }
}
/* Called from main context */
@@ -1539,26 +1604,17 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
}
/* Called from main context */
-static void sink_input_moved_cb(pa_sink_input *i) {
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
playback_stream *s;
pa_tagstruct *t;
- uint32_t maxlength, tlength, prebuf, minreq;
pa_sink_input_assert_ref(i);
s = PLAYBACK_STREAM(i->userdata);
playback_stream_assert_ref(s);
- maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq);
- tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq);
- prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq);
- minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq);
-
- fix_playback_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &tlength, &prebuf, &minreq);
- pa_memblockq_set_maxlength(s->memblockq, maxlength);
- pa_memblockq_set_tlength(s->memblockq, tlength);
- pa_memblockq_set_prebuf(s->memblockq, prebuf);
- pa_memblockq_set_minreq(s->memblockq, minreq);
- fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+ fix_playback_buffer_attr(s);
+ pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
if (s->connection->version < 12)
return;
@@ -1567,16 +1623,16 @@ static void sink_input_moved_cb(pa_sink_input *i) {
pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, i->sink->index);
- pa_tagstruct_puts(t, i->sink->name);
- pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED);
+ pa_tagstruct_putu32(t, dest->index);
+ pa_tagstruct_puts(t, dest->name);
+ pa_tagstruct_put_boolean(t, pa_sink_get_state(dest) == PA_SINK_SUSPENDED);
if (s->connection->version >= 13) {
- pa_tagstruct_putu32(t, maxlength);
- pa_tagstruct_putu32(t, tlength);
- pa_tagstruct_putu32(t, prebuf);
- pa_tagstruct_putu32(t, minreq);
- pa_tagstruct_put_usec(t, s->sink_latency);
+ pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.tlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
+ pa_tagstruct_putu32(t, s->buffer_attr.minreq);
+ pa_tagstruct_put_usec(t, s->configured_sink_latency);
}
pa_pstream_send_tagstruct(s->connection->pstream, t);
@@ -1585,6 +1641,27 @@ static void sink_input_moved_cb(pa_sink_input *i) {
/*** source_output callbacks ***/
/* Called from thread context */
+static int source_output_process_msg(pa_msgobject *_o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
+ pa_source_output *o = PA_SOURCE_OUTPUT(_o);
+ record_stream *s;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ switch (code) {
+ case SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY:
+ /* Atomically get a snapshot of all timing parameters... */
+ s->current_monitor_latency = o->source->monitor_of ? pa_sink_get_latency_within_thread(o->source->monitor_of) : 0;
+ s->current_source_latency = pa_source_get_latency_within_thread(o->source);
+ s->on_the_fly_snapshot = pa_atomic_load(&s->on_the_fly);
+ return 0;
+ }
+
+ return pa_source_output_process_msg(_o, code, userdata, offset, chunk);
+}
+
+/* Called from thread context */
static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
record_stream *s;
@@ -1593,6 +1670,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
record_stream_assert_ref(s);
pa_assert(chunk);
+ pa_atomic_add(&s->on_the_fly, chunk->length);
pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
}
@@ -1661,21 +1739,18 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
}
/* Called from main context */
-static void source_output_moved_cb(pa_source_output *o) {
+static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
record_stream *s;
pa_tagstruct *t;
- uint32_t maxlength, fragsize;
pa_source_output_assert_ref(o);
s = RECORD_STREAM(o->userdata);
record_stream_assert_ref(s);
- fragsize = (uint32_t) s->fragment_size;
- maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq);
-
- fix_record_buffer_attr_pre(s, TRUE, FALSE, &maxlength, &fragsize);
- pa_memblockq_set_maxlength(s->memblockq, maxlength);
- fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+ fix_record_buffer_attr_pre(s);
+ pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+ fix_record_buffer_attr_post(s);
if (s->connection->version < 12)
return;
@@ -1684,14 +1759,14 @@ static void source_output_moved_cb(pa_source_output *o) {
pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
pa_tagstruct_putu32(t, s->index);
- pa_tagstruct_putu32(t, o->source->index);
- pa_tagstruct_puts(t, o->source->name);
- pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED);
+ pa_tagstruct_putu32(t, dest->index);
+ pa_tagstruct_puts(t, dest->name);
+ pa_tagstruct_put_boolean(t, pa_source_get_state(dest) == PA_SOURCE_SUSPENDED);
if (s->connection->version >= 13) {
- pa_tagstruct_putu32(t, maxlength);
- pa_tagstruct_putu32(t, fragsize);
- pa_tagstruct_put_usec(t, s->source_latency);
+ pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
+ pa_tagstruct_put_usec(t, s->configured_source_latency);
}
pa_pstream_send_tagstruct(s->connection->pstream, t);
@@ -1723,7 +1798,8 @@ static pa_tagstruct *reply_new(uint32_t tag) {
static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
playback_stream *s;
- uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
+ uint32_t sink_index, syncid, missing;
+ pa_buffer_attr attr;
const char *name = NULL, *sink_name;
pa_sample_spec ss;
pa_channel_map map;
@@ -1752,6 +1828,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
pa_native_connection_assert_ref(c);
pa_assert(t);
+ memset(&attr, 0, sizeof(attr));
if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
pa_tagstruct_get(
@@ -1760,11 +1837,11 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
PA_TAG_CHANNEL_MAP, &map,
PA_TAG_U32, &sink_index,
PA_TAG_STRING, &sink_name,
- PA_TAG_U32, &maxlength,
+ PA_TAG_U32, &attr.maxlength,
PA_TAG_BOOLEAN, &corked,
- PA_TAG_U32, &tlength,
- PA_TAG_U32, &prebuf,
- PA_TAG_U32, &minreq,
+ PA_TAG_U32, &attr.tlength,
+ PA_TAG_U32, &attr.prebuf,
+ PA_TAG_U32, &attr.minreq,
PA_TAG_U32, &syncid,
PA_TAG_CVOLUME, &volume,
PA_TAG_INVALID) < 0) {
@@ -1875,7 +1952,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
* flag. For older versions we synthesize it here */
muted_set = muted_set || muted;
- s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret);
+ s = playback_stream_new(c, sink, &ss, &map, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret);
pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, ret);
@@ -1891,10 +1968,10 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
if (c->version >= 9) {
/* Since 0.9.0 we support sending the buffer metrics back to the client */
- pa_tagstruct_putu32(reply, (uint32_t) maxlength);
- pa_tagstruct_putu32(reply, (uint32_t) tlength);
- pa_tagstruct_putu32(reply, (uint32_t) prebuf);
- pa_tagstruct_putu32(reply, (uint32_t) minreq);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.tlength);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.prebuf);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.minreq);
}
if (c->version >= 12) {
@@ -1912,7 +1989,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
}
if (c->version >= 13)
- pa_tagstruct_put_usec(reply, s->sink_latency);
+ pa_tagstruct_put_usec(reply, s->configured_sink_latency);
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -1978,7 +2055,7 @@ static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t t
static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
record_stream *s;
- uint32_t maxlength, fragment_size;
+ pa_buffer_attr attr;
uint32_t source_index;
const char *name = NULL, *source_name;
pa_sample_spec ss;
@@ -2008,14 +2085,16 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
pa_native_connection_assert_ref(c);
pa_assert(t);
+ memset(&attr, 0, sizeof(attr));
+
if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
pa_tagstruct_get_channel_map(t, &map) < 0 ||
pa_tagstruct_getu32(t, &source_index) < 0 ||
pa_tagstruct_gets(t, &source_name) < 0 ||
- pa_tagstruct_getu32(t, &maxlength) < 0 ||
+ pa_tagstruct_getu32(t, &attr.maxlength) < 0 ||
pa_tagstruct_get_boolean(t, &corked) < 0 ||
- pa_tagstruct_getu32(t, &fragment_size) < 0) {
+ pa_tagstruct_getu32(t, &attr.fragsize) < 0) {
protocol_error(c);
return;
}
@@ -2125,7 +2204,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
(dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
(fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0);
- s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
+ s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, ret);
@@ -2138,8 +2217,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
if (c->version >= 9) {
/* Since 0.9 we support sending the buffer metrics back to the client */
- pa_tagstruct_putu32(reply, (uint32_t) maxlength);
- pa_tagstruct_putu32(reply, (uint32_t) fragment_size);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.fragsize);
}
if (c->version >= 12) {
@@ -2157,7 +2236,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
}
if (c->version >= 13)
- pa_tagstruct_put_usec(reply, s->source_latency);
+ pa_tagstruct_put_usec(reply, s->configured_source_latency);
pa_pstream_send_tagstruct(c->pstream, reply);
}
@@ -2444,7 +2523,6 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
playback_stream *s;
struct timeval tv, now;
uint32_t idx;
- pa_usec_t latency;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -2460,25 +2538,27 @@ static void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uin
s = pa_idxset_get_by_index(c->output_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- CHECK_VALIDITY(c->pstream, pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0, tag, PA_ERR_NOENTITY)
-
- reply = reply_new(tag);
-
- latency = pa_sink_get_latency(s->sink_input->sink);
- latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec);
- pa_tagstruct_put_usec(reply, latency);
+ /* Get an atomic snapshot of all timing parameters */
+ pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
+ reply = reply_new(tag);
+ pa_tagstruct_put_usec(reply,
+ s->current_sink_latency +
+ pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec));
pa_tagstruct_put_usec(reply, 0);
- pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0);
+ pa_tagstruct_put_boolean(reply,
+ s->playing_for > 0 &&
+ pa_sink_get_state(s->sink_input->sink) == PA_SINK_RUNNING &&
+ pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, s->write_index);
pa_tagstruct_puts64(reply, s->read_index);
if (c->version >= 13) {
- pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for);
- pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for);
+ pa_tagstruct_putu64(reply, s->underrun_for);
+ pa_tagstruct_putu64(reply, s->playing_for);
}
pa_pstream_send_tagstruct(c->pstream, reply);
@@ -2505,10 +2585,17 @@ static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint3
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+ /* Get an atomic snapshot of all timing parameters */
+ pa_assert_se(pa_asyncmsgq_send(s->source_output->source->asyncmsgq, PA_MSGOBJECT(s->source_output), SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
+
reply = reply_new(tag);
- pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0);
- pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source));
- pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING);
+ pa_tagstruct_put_usec(reply, s->current_monitor_latency);
+ pa_tagstruct_put_usec(reply,
+ s->current_source_latency +
+ pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->sample_spec));
+ pa_tagstruct_put_boolean(reply,
+ pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING &&
+ pa_source_output_get_state(s->source_output) == PA_SOURCE_OUTPUT_RUNNING);
pa_tagstruct_put_timeval(reply, &tv);
pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
@@ -2732,7 +2819,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
PA_TAG_SAMPLE_SPEC, &fixed_ss,
PA_TAG_CHANNEL_MAP, &sink->channel_map,
PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
- PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE),
+ PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE, FALSE),
PA_TAG_BOOLEAN, pa_sink_get_mute(sink, FALSE),
PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
@@ -2856,6 +2943,7 @@ static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_m
static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
pa_sample_spec fixed_ss;
pa_usec_t sink_latency;
+ pa_cvolume v;
pa_assert(t);
pa_sink_input_assert_ref(s);
@@ -2869,7 +2957,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
pa_tagstruct_putu32(t, s->sink->index);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
- pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s));
+ pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s, &v, TRUE));
pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
pa_tagstruct_put_usec(t, sink_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
@@ -3234,11 +3322,11 @@ static void command_set_volume(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_volume(sink, &volume, TRUE, TRUE);
+ pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE);
else if (source)
pa_source_set_volume(source, &volume);
else if (si)
- pa_sink_input_set_volume(si, &volume, TRUE);
+ pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
pa_pstream_send_simple_ack(c->pstream, tag);
}
@@ -3435,12 +3523,14 @@ static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint
static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
uint32_t idx;
- uint32_t maxlength, tlength, prebuf, minreq, fragsize;
+ pa_buffer_attr a;
pa_tagstruct *reply;
pa_native_connection_assert_ref(c);
pa_assert(t);
+ memset(&a, 0, sizeof(a));
+
if (pa_tagstruct_getu32(t, &idx) < 0) {
protocol_error(c);
return;
@@ -3458,10 +3548,10 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get(
t,
- PA_TAG_U32, &maxlength,
- PA_TAG_U32, &tlength,
- PA_TAG_U32, &prebuf,
- PA_TAG_U32, &minreq,
+ PA_TAG_U32, &a.maxlength,
+ PA_TAG_U32, &a.tlength,
+ PA_TAG_U32, &a.prebuf,
+ PA_TAG_U32, &a.minreq,
PA_TAG_INVALID) < 0 ||
(c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
(c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
@@ -3470,21 +3560,21 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
return;
}
- fix_playback_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &tlength, &prebuf, &minreq);
- pa_memblockq_set_maxlength(s->memblockq, maxlength);
- pa_memblockq_set_tlength(s->memblockq, tlength);
- pa_memblockq_set_prebuf(s->memblockq, prebuf);
- pa_memblockq_set_minreq(s->memblockq, minreq);
- fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq);
+ s->adjust_latency = adjust_latency;
+ s->early_requests = early_requests;
+ s->buffer_attr = a;
+
+ fix_playback_buffer_attr(s);
+ pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR, NULL, 0, NULL) == 0);
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, maxlength);
- pa_tagstruct_putu32(reply, tlength);
- pa_tagstruct_putu32(reply, prebuf);
- pa_tagstruct_putu32(reply, minreq);
+ pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(reply, s->buffer_attr.tlength);
+ pa_tagstruct_putu32(reply, s->buffer_attr.prebuf);
+ pa_tagstruct_putu32(reply, s->buffer_attr.minreq);
if (c->version >= 13)
- pa_tagstruct_put_usec(reply, s->sink_latency);
+ pa_tagstruct_put_usec(reply, s->configured_sink_latency);
} else {
record_stream *s;
@@ -3496,8 +3586,8 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
if (pa_tagstruct_get(
t,
- PA_TAG_U32, &maxlength,
- PA_TAG_U32, &fragsize,
+ PA_TAG_U32, &a.maxlength,
+ PA_TAG_U32, &a.fragsize,
PA_TAG_INVALID) < 0 ||
(c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
(c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
@@ -3506,16 +3596,21 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
return;
}
- fix_record_buffer_attr_pre(s, adjust_latency, early_requests, &maxlength, &fragsize);
- pa_memblockq_set_maxlength(s->memblockq, maxlength);
- fix_record_buffer_attr_post(s, &maxlength, &fragsize);
+ s->adjust_latency = adjust_latency;
+ s->early_requests = early_requests;
+ s->buffer_attr = a;
+
+ fix_record_buffer_attr_pre(s);
+ pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
+ pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
+ fix_record_buffer_attr_post(s);
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, maxlength);
- pa_tagstruct_putu32(reply, fragsize);
+ pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
+ pa_tagstruct_putu32(reply, s->buffer_attr.fragsize);
if (c->version >= 13)
- pa_tagstruct_put_usec(reply, s->source_latency);
+ pa_tagstruct_put_usec(reply, s->configured_source_latency);
}
pa_pstream_send_tagstruct(c->pstream, reply);
@@ -4119,7 +4214,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
- if (pa_card_set_profile(card, profile) < 0) {
+ if (pa_card_set_profile(card, profile, TRUE) < 0) {
pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
return;
}
@@ -4151,7 +4246,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_native_connection_assert_ref(c);
if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
- pa_log("client sent block for invalid stream.");
+ pa_log_debug("Client sent block for invalid stream.");
/* Ignoring */
return;
}
diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h
index 1e98832..782436b 100644
--- a/src/pulsecore/refcnt.h
+++ b/src/pulsecore/refcnt.h
@@ -23,23 +23,59 @@
***/
#include <pulsecore/atomic.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/log.h>
+
+/* #define DEBUG_REF */
#define PA_REFCNT_DECLARE \
pa_atomic_t _ref
-#define PA_REFCNT_INIT(p) \
- pa_atomic_store(&(p)->_ref, 1)
+#define PA_REFCNT_VALUE(p) \
+ pa_atomic_load(&(p)->_ref)
#define PA_REFCNT_INIT_ZERO(p) \
pa_atomic_store(&(p)->_ref, 0)
+#ifndef DEBUG_REF
+
+#define PA_REFCNT_INIT(p) \
+ pa_atomic_store(&(p)->_ref, 1)
+
#define PA_REFCNT_INC(p) \
pa_atomic_inc(&(p)->_ref)
#define PA_REFCNT_DEC(p) \
(pa_atomic_dec(&(p)->_ref)-1)
-#define PA_REFCNT_VALUE(p) \
- pa_atomic_load(&(p)->_ref)
+#else
+
+/* If you need to debug ref counting problems define DEBUG_REF and
+ * set $PULSE_LOG_BACKTRACE=5 or suchlike in the shell when running
+ * PA */
+
+#define PA_REFCNT_INIT(p) \
+ do { \
+ pa_atomic_store(&(p)->_ref, 1); \
+ pa_log("REF: Init %p", p); \
+ } while (FALSE)
+
+#define PA_REFCNT_INC(p) \
+ do { \
+ pa_atomic_inc(&(p)->_ref); \
+ pa_log("REF: Inc %p", p); \
+ } while (FALSE) \
+
+#define PA_REFCNT_DEC(p) \
+ ({ \
+ int _j = (pa_atomic_dec(&(p)->_ref)-1); \
+ if (_j <= 0) \
+ pa_log("REF: Done %p", p); \
+ else \
+ pa_log("REF: Dec %p", p); \
+ _j; \
+ })
+
+#endif
#endif
diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c
index 56ab2ef..65c826a 100644
--- a/src/pulsecore/rtclock.c
+++ b/src/pulsecore/rtclock.c
@@ -95,6 +95,8 @@ pa_bool_t pa_rtclock_hrtimer(void) {
#endif
}
+#define TIMER_SLACK_NS (int) ((500 * PA_NSEC_PER_USEC))
+
void pa_rtclock_hrtimer_enable(void) {
#ifdef PR_SET_TIMERSLACK
int slack_ns;
@@ -104,15 +106,17 @@ void pa_rtclock_hrtimer_enable(void) {
return;
}
- pa_log_debug("Timer slack set to %i us.", slack_ns/1000);
+ pa_log_debug("Timer slack is set to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
- slack_ns = 500000000;
+ if (slack_ns > TIMER_SLACK_NS) {
+ slack_ns = TIMER_SLACK_NS;
- pa_log_debug("Setting timer slack to %i us.", slack_ns/1000);
+ pa_log_debug("Setting timer slack to %i us.", (int) (slack_ns/PA_NSEC_PER_USEC));
- if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
- pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
- return;
+ if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
+ pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
+ return;
+ }
}
#endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 53e727b..0229918 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -117,7 +117,8 @@ static void reset_callbacks(pa_sink_input *i) {
i->attach = NULL;
i->detach = NULL;
i->suspend = NULL;
- i->moved = NULL;
+ i->suspend_within_thread = NULL;
+ i->moving = NULL;
i->kill = NULL;
i->get_latency = NULL;
i->state_change = NULL;
@@ -175,16 +176,8 @@ int pa_sink_input_new(
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
if (!data->volume_is_set) {
-
- if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
- data->volume = *pa_sink_get_volume(data->sink, FALSE);
- pa_cvolume_remap(&data->volume, &data->sink->channel_map, &data->channel_map);
- data->volume_is_absolute = TRUE;
- } else {
- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- data->volume_is_absolute = FALSE;
- }
-
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+ data->volume_is_absolute = FALSE;
data->save_volume = FALSE;
}
@@ -278,15 +271,15 @@ int pa_sink_input_new(
/* When the 'absolute' bool is not set then we'll treat the volume
* as relative to the sink volume even in flat volume mode */
- pa_cvolume t = *pa_sink_get_volume(data->sink, FALSE);
- pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map);
-
- pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &t);
+ pa_cvolume v = data->sink->reference_volume;
+ pa_cvolume_remap(&v, &data->sink->channel_map, &data->channel_map);
+ pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &v);
} else
i->virtual_volume = data->volume;
i->volume_factor = data->volume_factor;
pa_cvolume_init(&i->soft_volume);
+ memset(i->relative_volume, 0, sizeof(i->relative_volume));
i->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -333,8 +326,8 @@ int pa_sink_input_new(
0,
&i->sink->silence);
- pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
- pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
+ pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
+ pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0);
if (i->client)
pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
@@ -449,7 +442,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
}
if (i->sink->asyncmsgq)
@@ -527,9 +520,9 @@ void pa_sink_input_put(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
} else
- pa_sw_cvolume_multiply(&i->soft_volume, &i->virtual_volume, &i->volume_factor);
+ pa_sink_input_set_relative_volume(i, &i->virtual_volume);
i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
@@ -631,7 +624,7 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
* data, so let's just hand out silence */
pa_atomic_store(&i->thread_info.drained, 1);
- pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE);
+ pa_memblockq_seek(i->thread_info.render_memblockq, (int64_t) slength, PA_SEEK_RELATIVE, TRUE);
i->thread_info.playing_for = 0;
if (i->thread_info.underrun_for != (uint64_t) -1)
i->thread_info.underrun_for += ilength;
@@ -776,7 +769,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
if (amount > 0)
/* Ok, now update the write pointer */
- pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
+ pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE, TRUE);
if (i->thread_info.rewrite_flush)
pa_memblockq_silence(i->thread_info.render_memblockq);
@@ -819,26 +812,15 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the
}
/* Called from thread context */
-static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) {
- pa_sink_assert_ref(s);
-
- if (usec == (pa_usec_t) -1)
- return usec;
-
- if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
- usec = s->thread_info.max_latency;
-
- if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
- usec = s->thread_info.min_latency;
-
- return usec;
-}
-
-/* Called from thread context */
pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i);
- usec = fixup_latency(i->sink, usec);
+ if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+ usec = i->sink->fixed_latency;
+
+ if (usec != (pa_usec_t) -1)
+ usec = PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+
i->thread_info.requested_sink_latency = usec;
pa_sink_invalidate_requested_latency(i->sink);
@@ -849,41 +831,62 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i);
- if (PA_SINK_INPUT_IS_LINKED(i->state))
+ if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
- else
- /* If this sink input is not realized yet, we have to touch
- * the thread info data directly */
+ return usec;
+ }
- i->thread_info.requested_sink_latency = usec;
+ /* If this sink input is not realized yet or we are being moved,
+ * we have to touch the thread info data directly */
+
+ if (i->sink) {
+ if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+ usec = i->sink->fixed_latency;
+
+ if (usec != (pa_usec_t) -1) {
+ pa_usec_t min_latency, max_latency;
+ pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
+ usec = PA_CLAMP(usec, min_latency, max_latency);
+ }
+ }
+
+ i->thread_info.requested_sink_latency = usec;
return usec;
}
/* Called from main context */
pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
- pa_usec_t usec = 0;
-
pa_sink_input_assert_ref(i);
- if (PA_SINK_INPUT_IS_LINKED(i->state))
+ if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
+ pa_usec_t usec = 0;
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
- else
- /* If this sink input is not realized yet, we have to touch
- * the thread info data directly */
- usec = i->thread_info.requested_sink_latency;
+ return usec;
+ }
- return usec;
+ /* If this sink input is not realized yet or we are being moved,
+ * we have to touch the thread info data directly */
+
+ return i->thread_info.requested_sink_latency;
}
/* Called from main context */
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save) {
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
+ pa_cvolume v;
+
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(volume);
pa_assert(pa_cvolume_valid(volume));
pa_assert(pa_cvolume_compatible(volume, &i->sample_spec));
+ if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
+ v = i->sink->reference_volume;
+ pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
+ volume = pa_sw_cvolume_multiply(&v, &v, volume);
+ }
+
if (pa_cvolume_equal(volume, &i->virtual_volume))
return;
@@ -897,17 +900,18 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
* volumes and update the flat volume of the sink */
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE);
} else {
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
- pa_sw_cvolume_multiply(&i->soft_volume, volume, &i->volume_factor);
+ pa_sink_input_set_relative_volume(i, volume);
/* Hooks have the ability to play games with i->soft_volume */
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ /* Copy the new soft_volume to the thread_info struct */
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
}
@@ -916,11 +920,67 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
}
/* Called from main context */
-const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
+pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- return &i->virtual_volume;
+ if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
+ pa_cvolume v = i->sink->reference_volume;
+ pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
+ pa_sw_cvolume_divide(volume, &i->virtual_volume, &v);
+ } else
+ *volume = i->virtual_volume;
+
+ return volume;
+}
+
+/* Called from main context */
+pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
+ unsigned c;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(v);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+
+ /* This always returns the relative volume. Converts the float
+ * version into a pa_cvolume */
+
+ v->channels = i->sample_spec.channels;
+
+ for (c = 0; c < v->channels; c++)
+ v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]);
+
+ return v;
+}
+
+/* Called from main context */
+void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v) {
+ unsigned c;
+ pa_cvolume _v;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
+
+ if (!v)
+ v = pa_cvolume_reset(&_v, i->sample_spec.channels);
+
+ /* This basically calculates:
+ *
+ * i->relative_volume := v
+ * i->soft_volume := i->relative_volume * i->volume_factor */
+
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++) {
+ i->relative_volume[c] = pa_sw_volume_to_linear(v->values[c]);
+
+ i->soft_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(i->volume_factor.values[c]));
+ }
+
+ /* We don't copy the data to the thread_info data. That's left for someone else to do */
}
/* Called from main context */
@@ -1090,14 +1150,16 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
- /* Make the absolute volume relative */
- i->virtual_volume = i->soft_volume;
- i->soft_volume = i->volume_factor;
+ /* Make the virtual volume relative */
+ pa_sink_input_get_relative_volume(i, &i->virtual_volume);
+
+ /* And reset the the relative volume */
+ pa_sink_input_set_relative_volume(i, NULL);
/* We might need to update the sink's volume if we are in flat
* volume mode. */
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
@@ -1105,6 +1167,8 @@ int pa_sink_input_start_move(pa_sink_input *i) {
pa_sink_update_status(i->sink);
i->sink = NULL;
+ pa_sink_input_unref(i);
+
return 0;
}
@@ -1147,9 +1211,12 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
} else
new_resampler = NULL;
+ if (i->moving)
+ i->moving(i, dest);
+
i->sink = dest;
i->save_sink = save;
- pa_idxset_put(dest->inputs, i, NULL);
+ pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
i->sink->n_corked++;
@@ -1173,20 +1240,19 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
0,
&i->sink->silence);
}
-
pa_sink_update_status(dest);
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
/* Make relative volume absolute again */
- pa_cvolume t = dest->virtual_volume;
+ pa_cvolume t = dest->reference_volume;
pa_cvolume_remap(&t, &dest->channel_map, &i->channel_map);
pa_sw_cvolume_multiply(&i->virtual_volume, &i->virtual_volume, &t);
/* We might need to update the sink's volume if we are in flat volume mode. */
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
@@ -1194,9 +1260,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
/* Notify everyone */
- if (i->moved)
- i->moved(i);
-
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
@@ -1218,11 +1281,19 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
if (!pa_sink_input_may_move_to(i, dest))
return -PA_ERR_NOTSUPPORTED;
- if ((r = pa_sink_input_start_move(i)) < 0)
+ pa_sink_input_ref(i);
+
+ if ((r = pa_sink_input_start_move(i)) < 0) {
+ pa_sink_input_unref(i);
return r;
+ }
- if ((r = pa_sink_input_finish_move(i, dest, save)) < 0)
+ if ((r = pa_sink_input_finish_move(i, dest, save)) < 0) {
+ pa_sink_input_unref(i);
return r;
+ }
+
+ pa_sink_input_unref(i);
return 0;
}
@@ -1291,12 +1362,9 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
- pa_usec_t sink_usec = 0;
r[0] += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec);
-
- if (i->sink->parent.process_msg(PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_usec, 0, NULL) >= 0)
- r[1] += sink_usec;
+ r[1] += pa_sink_get_latency_within_thread(i->sink);
return 0;
}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index e380168..98144d4 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -91,7 +91,11 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
- pa_cvolume virtual_volume, soft_volume, volume_factor;
+ /* Also see http://pulseaudio.org/wiki/InternalVolumes */
+ pa_cvolume virtual_volume; /* The volume clients are informed about */
+ pa_cvolume volume_factor; /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
+ double relative_volume[PA_CHANNELS_MAX]; /* The calculated volume relative to the sink volume as linear factors. */
+ pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as relative_volume * volume_factor */
pa_bool_t muted:1;
/* if TRUE then the source we are connected to and/or the volume
@@ -121,7 +125,7 @@ struct pa_sink_input {
* changes. Called from IO context. */
void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */
- /* Called whenever the maxiumum request size of the sink
+ /* Called whenever the maximum request size of the sink
* changes. Called from IO context. */
void (*update_max_request) (pa_sink_input *i, size_t nbytes); /* may be NULL */
@@ -144,13 +148,19 @@ struct pa_sink_input {
* disconnected from its sink. Called from IO thread context */
void (*detach) (pa_sink_input *i); /* may be NULL */
- /* If non-NULL called whenever the the sink this input is attached
+ /* If non-NULL called whenever the sink this input is attached
* to suspends or resumes. Called from main context */
void (*suspend) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
- /* If non-NULL called whenever the the sink this input is attached
- * to changes. Called from main context */
- void (*moved) (pa_sink_input *i); /* may be NULL */
+ /* If non-NULL called whenever the sink this input is attached
+ * to suspends or resumes. Called from IO context */
+ void (*suspend_within_thread) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+
+ /* If non-NULL called whenever the sink input is moved to a new
+ * sink. Called from main context after the sink input has been
+ * detached from the old sink and before it has been attached to
+ * the new sink. */
+ void (*moving) (pa_sink_input *i, pa_sink *dest); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
* context. */
@@ -300,10 +310,14 @@ void pa_sink_input_kill(pa_sink_input*i);
pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save);
-const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
+pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute);
+
+pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v);
+
void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
+
void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
@@ -342,4 +356,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
+/* To be used by sink.c only */
+void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v);
+
#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index ff8700c..30fa557 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -48,7 +48,9 @@
#define MAX_MIX_CHANNELS 32
#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
-#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+#define ABSOLUTE_MIN_LATENCY (500)
+#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
@@ -200,13 +202,15 @@ pa_sink* pa_sink_new(
s->inputs = pa_idxset_new(NULL, NULL);
s->n_corked = 0;
- s->virtual_volume = data->volume;
+ s->reference_volume = s->virtual_volume = data->volume;
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->base_volume = PA_VOLUME_NORM;
s->n_volume_steps = PA_VOLUME_NORM+1;
s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
+ s->fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
reset_callbacks(s);
s->userdata = NULL;
@@ -230,8 +234,8 @@ pa_sink* pa_sink_new(
s->thread_info.max_request = 0;
s->thread_info.requested_latency_valid = FALSE;
s->thread_info.requested_latency = 0;
- s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
- s->thread_info.max_latency = 0;
+ s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
+ s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
@@ -259,7 +263,7 @@ pa_sink* pa_sink_new(
pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor");
- s->monitor_source = pa_source_new(core, &source_data, PA_SOURCE_LATENCY);
+ s->monitor_source = pa_source_new(core, &source_data, 0);
pa_source_new_data_done(&source_data);
@@ -343,16 +347,16 @@ void pa_sink_put(pa_sink* s) {
/* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
pa_assert(s->rtpoll);
- pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
- s->thread_info.min_latency <= s->thread_info.max_latency);
+ pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
s->flags |= PA_SINK_DECIBEL_VOLUME;
-
- s->thread_info.soft_volume = s->soft_volume;
- s->thread_info.soft_muted = s->muted;
+ s->base_volume = PA_VOLUME_NORM;
}
+ s->thread_info.soft_volume = s->soft_volume;
+ s->thread_info.soft_muted = s->muted;
+
if (s->flags & PA_SINK_DECIBEL_VOLUME)
s->n_volume_steps = PA_VOLUME_NORM+1;
@@ -360,6 +364,17 @@ void pa_sink_put(pa_sink* s) {
if (s->flags & PA_SINK_DECIBEL_VOLUME)
s->flags |= PA_SINK_FLAT_VOLUME;
+ if (s->flags & PA_SINK_LATENCY)
+ s->monitor_source->flags |= PA_SOURCE_LATENCY;
+
+ if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
+ s->monitor_source->flags |= PA_SOURCE_DYNAMIC_LATENCY;
+ s->fixed_latency = 0;
+ } else if (s->fixed_latency <= 0)
+ s->fixed_latency = DEFAULT_FIXED_LATENCY;
+
+ s->monitor_source->fixed_latency = s->fixed_latency;
+
pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
pa_source_put(s->monitor_source);
@@ -509,8 +524,12 @@ pa_queue *pa_sink_move_all_start(pa_sink *s) {
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
+ pa_sink_input_ref(i);
+
if (pa_sink_input_start_move(i) >= 0)
- pa_queue_push(q, pa_sink_input_ref(i));
+ pa_queue_push(q, i);
+ else
+ pa_sink_input_unref(i);
}
return q;
@@ -952,6 +971,72 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
return usec;
}
+/* Called from IO thread */
+pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
+ pa_usec_t usec = 0;
+ pa_msgobject *o;
+
+ pa_sink_assert_ref(s);
+ pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
+
+ /* The returned value is supposed to be in the time domain of the sound card! */
+
+ if (s->thread_info.state == PA_SINK_SUSPENDED)
+ return 0;
+
+ if (!(s->flags & PA_SINK_LATENCY))
+ return 0;
+
+ o = PA_MSGOBJECT(s);
+
+ /* We probably should make this a proper vtable callback instead of going through process_msg() */
+
+ if (o->process_msg(o, PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ return -1;
+
+ return usec;
+}
+
+static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) {
+ unsigned c;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(new_volume->channels == i->sample_spec.channels);
+
+ /*
+ * This basically calculates:
+ *
+ * i->relative_volume := i->virtual_volume / new_volume
+ * i->soft_volume := i->relative_volume * i->volume_factor
+ */
+
+ /* The new sink volume passed in here must already be remapped to
+ * the sink input's channel map! */
+
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++)
+
+ if (new_volume->values[c] <= PA_VOLUME_MUTED)
+ /* We leave i->relative_volume untouched */
+ i->soft_volume.values[c] = PA_VOLUME_MUTED;
+ else {
+ i->relative_volume[c] =
+ pa_sw_volume_to_linear(i->virtual_volume.values[c]) /
+ pa_sw_volume_to_linear(new_volume->values[c]);
+
+ i->soft_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(i->volume_factor.values[c]));
+ }
+
+ /* Hooks have the ability to play games with i->soft_volume */
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+
+ /* We don't copy the soft_volume to the thread_info data
+ * here. That must be done by the caller */
+}
+
/* Called from main thread */
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
pa_sink_input *i;
@@ -962,16 +1047,16 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
- /* This is called whenever a sink input volume changes and we
- * might need to fix up the sink volume accordingly. Please note
- * that we don't actually update the sinks volume here, we only
- * return how it needs to be updated. The caller should then call
- * pa_sink_set_flat_volume().*/
+ /* This is called whenever a sink input volume changes or a sink
+ * input is added/removed and we might need to fix up the sink
+ * volume accordingly. Please note that we don't actually update
+ * the sinks volume here, we only return how it needs to be
+ * updated. The caller should then call pa_sink_set_volume().*/
if (pa_idxset_isempty(s->inputs)) {
/* In the special case that we have no sink input we leave the
* volume unmodified. */
- *new_volume = s->virtual_volume;
+ *new_volume = s->reference_volume;
return;
}
@@ -998,26 +1083,22 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
remapped_new_volume = *new_volume;
pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume);
- pa_sw_cvolume_multiply(&i->soft_volume, &i->soft_volume, &i->volume_factor);
+ compute_new_soft_volume(i, &remapped_new_volume);
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
-
- /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because
- * we want the update to have atomically with the sink
- * volume update, hence we do it within the
- * pa_sink_set_flat_volume() call below*/
+ /* We don't copy soft_volume to the thread_info data here
+ * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
+ * want the update to be atomically with the sink volume
+ * update, hence we do it within the pa_sink_set_volume() call
+ * below */
}
}
/* Called from main thread */
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
+void pa_sink_propagate_flat_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
pa_sink_assert_ref(s);
- pa_assert(old_volume);
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
@@ -1026,39 +1107,43 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
* sink input volumes accordingly */
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume remapped_old_volume, remapped_new_volume, fixed_volume;
+ pa_cvolume sink_volume, new_virtual_volume;
unsigned c;
- remapped_new_volume = s->virtual_volume;
- pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
+ /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
- remapped_old_volume = *old_volume;
- pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map);
+ sink_volume = s->virtual_volume;
+ pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
for (c = 0; c < i->sample_spec.channels; c++)
+ new_virtual_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(sink_volume.values[c]));
- if (remapped_old_volume.values[c] == PA_VOLUME_MUTED)
- fixed_volume.values[c] = PA_VOLUME_MUTED;
- else
- fixed_volume.values[c] = (pa_volume_t)
- ((uint64_t) i->virtual_volume.values[c] *
- (uint64_t) remapped_new_volume.values[c] /
- (uint64_t) remapped_old_volume.values[c]);
+ new_virtual_volume.channels = i->sample_spec.channels;
- fixed_volume.channels = i->virtual_volume.channels;
+ if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) {
+ i->virtual_volume = new_virtual_volume;
- if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) {
- i->virtual_volume = fixed_volume;
+ /* Hmm, the soft volume might no longer actually match
+ * what has been chosen as new virtual volume here,
+ * especially when the old volume was
+ * PA_VOLUME_MUTED. Hence let's recalculate the soft
+ * volumes here. */
+ compute_new_soft_volume(i, &sink_volume);
/* The virtual volume changed, let's tell people so */
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
+
+ /* If the soft_volume of any of the sink inputs got changed, let's
+ * make sure the thread copies are synced up. */
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SYNC_VOLUMES, NULL, 0, NULL) == 0);
}
/* Called from main thread */
-void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) {
- pa_cvolume old_virtual_volume;
+void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference) {
pa_bool_t virtual_volume_changed;
pa_sink_assert_ref(s);
@@ -1067,19 +1152,21 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
pa_assert(pa_cvolume_valid(volume));
pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
- old_virtual_volume = s->virtual_volume;
+ virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
s->virtual_volume = *volume;
- virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
+
+ if (become_reference)
+ s->reference_volume = s->virtual_volume;
/* Propagate this volume change back to the inputs */
if (virtual_volume_changed)
if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
- pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+ pa_sink_propagate_flat_volume(s);
if (s->set_volume) {
/* If we have a function set_volume(), then we do not apply a
- * soft volume by default. However, set_volume() is apply one
- * to s->soft_volume */
+ * soft volume by default. However, set_volume() is free to
+ * apply one to s->soft_volume */
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
s->set_volume(s);
@@ -1111,7 +1198,7 @@ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
}
/* Called from main thread */
-const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
+const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_bool_t reference) {
pa_sink_assert_ref(s);
if (s->refresh_volume || force_refresh) {
@@ -1124,14 +1211,33 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
+ s->reference_volume = s->virtual_volume;
+
if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+ pa_sink_propagate_flat_volume(s);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
}
- return &s->virtual_volume;
+ return reference ? &s->reference_volume : &s->virtual_volume;
+}
+
+/* Called from main thread */
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
+ pa_sink_assert_ref(s);
+
+ /* The sink implementor may call this if the volume changed to make sure everyone is notified */
+
+ if (pa_cvolume_equal(&s->virtual_volume, new_volume))
+ return;
+
+ s->reference_volume = s->virtual_volume = *new_volume;
+
+ if (s->flags & PA_SINK_FLAT_VOLUME)
+ pa_sink_propagate_flat_volume(s);
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from main thread */
@@ -1174,12 +1280,24 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) {
+ pa_sink_assert_ref(s);
+
+ /* The sink implementor may call this if the volume changed to make sure everyone is notified */
+ if (s->muted == new_muted)
+ return;
+
+ s->muted = new_muted;
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
+pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
pa_sink_assert_ref(s);
- pa_assert(p);
- pa_proplist_update(s->proplist, mode, p);
+ if (p)
+ pa_proplist_update(s->proplist, mode, p);
if (PA_SINK_IS_LINKED(s->state)) {
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
@@ -1508,9 +1626,13 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_sink_request_rewind(s, (size_t) -1);
}
- if (s->flags & PA_SINK_FLAT_VOLUME)
- sync_input_volumes_within_thread(s);
+ if (!(s->flags & PA_SINK_FLAT_VOLUME))
+ return 0;
+ /* Fall through ... */
+
+ case PA_SINK_MESSAGE_SYNC_VOLUMES:
+ sync_input_volumes_within_thread(s);
return 0;
case PA_SINK_MESSAGE_GET_VOLUME:
@@ -1528,7 +1650,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_GET_MUTE:
return 0;
- case PA_SINK_MESSAGE_SET_STATE:
+ case PA_SINK_MESSAGE_SET_STATE: {
+
+ pa_bool_t suspend_change =
+ (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+ (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
s->thread_info.state = PA_PTR_TO_UINT(userdata);
@@ -1537,7 +1663,17 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
s->thread_info.rewind_requested = FALSE;
}
+ if (suspend_change) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->suspend_within_thread)
+ i->suspend_within_thread(i, s->thread_info.state == PA_SINK_SUSPENDED);
+ }
+
return 0;
+ }
case PA_SINK_MESSAGE_DETACH:
@@ -1565,7 +1701,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_SET_LATENCY_RANGE: {
pa_usec_t *r = userdata;
- pa_sink_update_latency_range(s, r[0], r[1]);
+ pa_sink_set_latency_range_within_thread(s, r[0], r[1]);
return 0;
}
@@ -1589,6 +1725,16 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
*((size_t*) userdata) = s->thread_info.max_request;
return 0;
+ case PA_SINK_MESSAGE_SET_MAX_REWIND:
+
+ pa_sink_set_max_rewind_within_thread(s, (size_t) offset);
+ return 0;
+
+ case PA_SINK_MESSAGE_SET_MAX_REQUEST:
+
+ pa_sink_set_max_request_within_thread(s, (size_t) offset);
+ return 0;
+
case PA_SINK_MESSAGE_GET_LATENCY:
case PA_SINK_MESSAGE_MAX:
;
@@ -1696,6 +1842,9 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
pa_sink_assert_ref(s);
+ if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
if (s->thread_info.requested_latency_valid)
return s->thread_info.requested_latency;
@@ -1711,13 +1860,8 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
(result == (pa_usec_t) -1 || result > monitor_latency))
result = monitor_latency;
- if (result != (pa_usec_t) -1) {
- if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
- result = s->thread_info.max_latency;
-
- if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
- result = s->thread_info.min_latency;
- }
+ if (result != (pa_usec_t) -1)
+ result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
s->thread_info.requested_latency = result;
s->thread_info.requested_latency_valid = TRUE;
@@ -1740,7 +1884,7 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
}
/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
-void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind) {
pa_sink_input *i;
void *state = NULL;
@@ -1757,11 +1901,21 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
}
if (s->monitor_source)
- pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind);
+ pa_source_set_max_rewind_within_thread(s->monitor_source, s->thread_info.max_rewind);
+}
+
+/* Called from main thread */
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) {
+ pa_sink_assert_ref(s);
+
+ if (PA_SINK_IS_LINKED(s->state))
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
+ else
+ pa_sink_set_max_rewind_within_thread(s, max_rewind);
}
/* Called from IO as well as the main thread -- the latter only before the IO thread started up */
-void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request) {
void *state = NULL;
pa_sink_assert_ref(s);
@@ -1779,6 +1933,16 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
}
}
+/* Called from main thread */
+void pa_sink_set_max_request(pa_sink *s, size_t max_request) {
+ pa_sink_assert_ref(s);
+
+ if (PA_SINK_IS_LINKED(s->state))
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MAX_REQUEST, NULL, max_request, NULL) == 0);
+ else
+ pa_sink_set_max_request_within_thread(s, max_request);
+}
+
/* Called from IO thread */
void pa_sink_invalidate_requested_latency(pa_sink *s) {
pa_sink_input *i;
@@ -1786,14 +1950,20 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) {
pa_sink_assert_ref(s);
+ if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ return;
+
s->thread_info.requested_latency_valid = FALSE;
- if (s->update_requested_latency)
- s->update_requested_latency(s);
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
- if (i->update_sink_requested_latency)
- i->update_sink_requested_latency(i);
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->update_sink_requested_latency)
+ i->update_sink_requested_latency(i);
+ }
}
/* Called from main thread */
@@ -1801,19 +1971,23 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
pa_sink_assert_ref(s);
/* min_latency == 0: no limit
- * min_latency == (size_t) -1: default limit
* min_latency anything else: specified limit
*
* Similar for max_latency */
- if (min_latency == (pa_usec_t) -1)
- min_latency = DEFAULT_MIN_LATENCY;
+ if (min_latency < ABSOLUTE_MIN_LATENCY)
+ min_latency = ABSOLUTE_MIN_LATENCY;
+
+ if (max_latency <= 0 ||
+ max_latency > ABSOLUTE_MAX_LATENCY)
+ max_latency = ABSOLUTE_MAX_LATENCY;
- if (max_latency == (pa_usec_t) -1)
- max_latency = min_latency;
+ pa_assert(min_latency <= max_latency);
- pa_assert(!min_latency || !max_latency ||
- min_latency <= max_latency);
+ /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
+ pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+ max_latency == ABSOLUTE_MAX_LATENCY) ||
+ (s->flags & PA_SINK_DYNAMIC_LATENCY));
if (PA_SINK_IS_LINKED(s->state)) {
pa_usec_t r[2];
@@ -1822,15 +1996,8 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
r[1] = max_latency;
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
- } else {
- s->thread_info.min_latency = min_latency;
- s->thread_info.max_latency = max_latency;
-
- s->monitor_source->thread_info.min_latency = min_latency;
- s->monitor_source->thread_info.max_latency = max_latency;
-
- s->thread_info.requested_latency_valid = s->monitor_source->thread_info.requested_latency_valid = FALSE;
- }
+ } else
+ pa_sink_set_latency_range_within_thread(s, min_latency, max_latency);
}
/* Called from main thread */
@@ -1853,25 +2020,34 @@ void pa_sink_get_latency_range(pa_sink *s, pa_usec_t *min_latency, pa_usec_t *ma
}
/* Called from IO thread */
-void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
- pa_sink_input *i;
+void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency) {
void *state = NULL;
pa_sink_assert_ref(s);
- pa_assert(!min_latency || !max_latency ||
- min_latency <= max_latency);
+ pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
+ pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
+ pa_assert(min_latency <= max_latency);
+
+ /* Hmm, let's see if someone forgot to set PA_SINK_DYNAMIC_LATENCY here... */
+ pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+ max_latency == ABSOLUTE_MAX_LATENCY) ||
+ (s->flags & PA_SINK_DYNAMIC_LATENCY));
s->thread_info.min_latency = min_latency;
s->thread_info.max_latency = max_latency;
- while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
- if (i->update_sink_latency_range)
- i->update_sink_latency_range(i);
+ if (PA_SINK_IS_LINKED(s->thread_info.state)) {
+ pa_sink_input *i;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->update_sink_latency_range)
+ i->update_sink_latency_range(i);
+ }
pa_sink_invalidate_requested_latency(s);
- pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
+ pa_source_set_latency_range_within_thread(s->monitor_source, min_latency, max_latency);
}
/* Called from main context */
@@ -1921,6 +2097,8 @@ pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
t = "phone";
else if (pa_streq(ff, "portable"))
t = "multimedia-player";
+ else if (pa_streq(ff, "tv"))
+ t = "video-display";
}
if (!t)
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 0d33679..352282b 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -74,7 +74,10 @@ struct pa_sink {
pa_volume_t base_volume; /* shall be constant */
unsigned n_volume_steps; /* shall be constant */
- pa_cvolume virtual_volume, soft_volume;
+ /* Also see http://pulseaudio.org/wiki/InternalVolumes */
+ pa_cvolume virtual_volume; /* The volume clients are informed about */
+ pa_cvolume reference_volume; /* The volume taken as refernce base for relative sink input volumes */
+ pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through */
pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
@@ -85,6 +88,8 @@ struct pa_sink {
pa_memchunk silence;
+ pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -159,6 +164,7 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_REMOVE_INPUT,
PA_SINK_MESSAGE_GET_VOLUME,
PA_SINK_MESSAGE_SET_VOLUME,
+ PA_SINK_MESSAGE_SYNC_VOLUMES,
PA_SINK_MESSAGE_GET_MUTE,
PA_SINK_MESSAGE_SET_MUTE,
PA_SINK_MESSAGE_GET_LATENCY,
@@ -172,6 +178,8 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_GET_LATENCY_RANGE,
PA_SINK_MESSAGE_GET_MAX_REWIND,
PA_SINK_MESSAGE_GET_MAX_REQUEST,
+ PA_SINK_MESSAGE_SET_MAX_REWIND,
+ PA_SINK_MESSAGE_SET_MAX_REQUEST,
PA_SINK_MESSAGE_MAX
} pa_sink_message_t;
@@ -218,12 +226,16 @@ void pa_sink_set_description(pa_sink *s, const char *description);
void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q);
void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p);
+void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
+void pa_sink_set_max_request(pa_sink *s, size_t max_request);
void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume);
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted);
pa_bool_t pa_device_init_description(pa_proplist *p);
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink);
@@ -243,10 +255,11 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume);
+void pa_sink_propagate_flat_volume(pa_sink *s);
+
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference);
+const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference);
-void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg);
-const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
@@ -278,10 +291,10 @@ void pa_sink_detach_within_thread(pa_sink *s);
pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s);
-void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind);
-void pa_sink_set_max_request(pa_sink *s, size_t max_request);
+void pa_sink_set_max_rewind_within_thread(pa_sink *s, size_t max_rewind);
+void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request);
-void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
/*** To be called exclusively by sink input drivers, from IO context */
@@ -289,4 +302,6 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
void pa_sink_invalidate_requested_latency(pa_sink *s);
+pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
+
#endif
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index 6a4405e..e660700 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -467,11 +467,13 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port));
} else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) {
- char hn[256];
- if (!pa_get_host_name(hn, sizeof(hn)))
+ char *id;
+
+ if (!(id = pa_machine_id()))
return NULL;
- pa_snprintf(c, l, "{%s}tcp6:localhost:%u", hn, (unsigned) ntohs(sa.sin6_port));
+ pa_snprintf(c, l, "{%s}tcp6:localhost:%u", id, (unsigned) ntohs(sa.sin6_port));
+ pa_xfree(id);
} else {
char ip[INET6_ADDRSTRLEN];
@@ -503,11 +505,13 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port));
} else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) {
- char hn[256];
- if (!pa_get_host_name(hn, sizeof(hn)))
+ char *id;
+
+ if (!(id = pa_machine_id()))
return NULL;
- pa_snprintf(c, l, "{%s}tcp:localhost:%u", hn, (unsigned) ntohs(sa.sin_port));
+ pa_snprintf(c, l, "{%s}tcp:localhost:%u", id, (unsigned) ntohs(sa.sin_port));
+ pa_xfree(id);
} else {
char ip[INET_ADDRSTRLEN];
@@ -523,15 +527,16 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
}
case SOCKET_SERVER_UNIX: {
- char hn[256];
+ char *id;
if (!s->filename)
return NULL;
- if (!pa_get_host_name(hn, sizeof(hn)))
+ if (!(id = pa_machine_id()))
return NULL;
- pa_snprintf(c, l, "{%s}unix:%s", hn, s->filename);
+ pa_snprintf(c, l, "{%s}unix:%s", id, s->filename);
+ pa_xfree(id);
return c;
}
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 373d563..4ba25ae 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -87,7 +87,8 @@ static void reset_callbacks(pa_source_output *o) {
o->attach = NULL;
o->detach = NULL;
o->suspend = NULL;
- o->moved = NULL;
+ o->suspend_within_thread = NULL;
+ o->moving = NULL;
o->kill = NULL;
o->get_latency = NULL;
o->state_change = NULL;
@@ -434,11 +435,30 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
pa_log_debug("Delay queue overflow!");
- pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE);
+ pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
}
limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
+ if (limit > 0 && o->source->monitor_of) {
+ pa_usec_t latency;
+ size_t n;
+
+ /* Hmm, check the latency for knowing how much of the buffered
+ * data is actually still unplayed and might hence still
+ * change. This is suboptimal. Ideally we'd have a call like
+ * pa_sink_get_changeable_size() or so that tells us how much
+ * of the queued data is actually still changeable. Hence
+ * FIXME! */
+
+ latency = pa_sink_get_latency_within_thread(o->source->monitor_of);
+
+ n = pa_usec_to_bytes(latency, &o->source->sample_spec);
+
+ if (n < limit)
+ limit = n;
+ }
+
/* Implement the delay queue */
while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
pa_memchunk qchunk;
@@ -516,26 +536,15 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i
}
/* Called from thread context */
-static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) {
- pa_source_assert_ref(s);
-
- if (usec == (pa_usec_t) -1)
- return usec;
-
- if (s->thread_info.max_latency > 0 && usec > s->thread_info.max_latency)
- usec = s->thread_info.max_latency;
-
- if (s->thread_info.min_latency > 0 && usec < s->thread_info.min_latency)
- usec = s->thread_info.min_latency;
-
- return usec;
-}
-
-/* Called from thread context */
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o);
- usec = fixup_latency(o->source, usec);
+ if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ usec = o->source->fixed_latency;
+
+ if (usec != (pa_usec_t) -1)
+ usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
+
o->thread_info.requested_source_latency = usec;
pa_source_invalidate_requested_latency(o->source);
@@ -546,31 +555,44 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o);
- if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
- else
- /* If this source output is not realized yet, we have to touch
- * the thread info data directly */
+ return usec;
+ }
- o->thread_info.requested_source_latency = usec;
+ /* If this source output is not realized yet or is being moved, we
+ * have to touch the thread info data directly */
+
+ if (o->source) {
+ if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ usec = o->source->fixed_latency;
+
+ if (usec != (pa_usec_t) -1) {
+ pa_usec_t min_latency, max_latency;
+ pa_source_get_latency_range(o->source, &min_latency, &max_latency);
+ usec = PA_CLAMP(usec, min_latency, max_latency);
+ }
+ }
+
+ o->thread_info.requested_source_latency = usec;
return usec;
}
/* Called from main context */
pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
- pa_usec_t usec = 0;
-
pa_source_output_assert_ref(o);
- if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
+ pa_usec_t usec = 0;
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
- else
- /* If this source output is not realized yet, we have to touch
- * the thread info data directly */
- usec = o->thread_info.requested_source_latency;
+ return usec;
+ }
- return usec;
+ /* If this source output is not realized yet or is being moved, we
+ * have to touch the thread info data directly */
+
+ return o->thread_info.requested_source_latency;
}
/* Called from main context */
@@ -707,6 +729,8 @@ int pa_source_output_start_move(pa_source_output *o) {
pa_source_update_status(o->source);
o->source = NULL;
+ pa_source_output_unref(o);
+
return 0;
}
@@ -749,9 +773,12 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
} else
new_resampler = NULL;
+ if (o->moving)
+ o->moving(o, dest);
+
o->source = dest;
o->save_source = save;
- pa_idxset_put(o->source->outputs, o, NULL);
+ pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);
if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
o->source->n_corked++;
@@ -776,14 +803,12 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
}
pa_source_update_status(dest);
+
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);
/* Notify everyone */
- if (o->moved)
- o->moved(o);
-
pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);
pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
@@ -805,11 +830,19 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
if (!pa_source_output_may_move_to(o, dest))
return -PA_ERR_NOTSUPPORTED;
- if ((r = pa_source_output_start_move(o)) < 0)
+ pa_source_output_ref(o);
+
+ if ((r = pa_source_output_start_move(o)) < 0) {
+ pa_source_output_unref(o);
return r;
+ }
- if ((r = pa_source_output_finish_move(o, dest, save)) < 0)
+ if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
+ pa_source_output_unref(o);
return r;
+ }
+
+ pa_source_output_unref(o);
return 0;
}
@@ -836,12 +869,9 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: {
pa_usec_t *r = userdata;
- pa_usec_t source_usec = 0;
r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec);
-
- if (o->source->parent.process_msg(PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_usec, 0, NULL) >= 0)
- r[1] += source_usec;
+ r[1] += pa_source_get_latency_within_thread(o->source);
return 0;
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 018ec88..9824e16 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -116,13 +116,19 @@ struct pa_source_output {
* disconnected from its source. Called from IO thread context */
void (*detach) (pa_source_output *o); /* may be NULL */
- /* If non-NULL called whenever the the source this output is attached
+ /* If non-NULL called whenever the source this output is attached
* to suspends or resumes. Called from main context */
void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
- /* If non-NULL called whenever the the source this output is attached
- * to changes. Called from main context */
- void (*moved) (pa_source_output *o); /* may be NULL */
+ /* If non-NULL called whenever the source this output is attached
+ * to suspends or resumes. Called from IO context */
+ void (*suspend_within_thread) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+
+ /* If non-NULL called whenever the source output is moved to a new
+ * source. Called from main context after the stream was detached
+ * from the old source and before it is attached to the new
+ * source. */
+ void (*moving) (pa_source_output *o, pa_source *dest); /* may be NULL */
/* Supposed to unlink and destroy this stream. Called from main
* context. */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index cc6dfc4..2190250 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -41,7 +41,9 @@
#include "source.h"
-#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC)
+#define ABSOLUTE_MIN_LATENCY (500)
+#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
@@ -198,6 +200,8 @@ pa_source* pa_source_new(
s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
+ s->fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
reset_callbacks(s);
s->userdata = NULL;
@@ -218,8 +222,8 @@ pa_source* pa_source_new(
s->thread_info.max_rewind = 0;
s->thread_info.requested_latency_valid = FALSE;
s->thread_info.requested_latency = 0;
- s->thread_info.min_latency = DEFAULT_MIN_LATENCY;
- s->thread_info.max_latency = 0;
+ s->thread_info.min_latency = ABSOLUTE_MIN_LATENCY;
+ s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
@@ -302,8 +306,7 @@ void pa_source_put(pa_source *s) {
/* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
pa_assert(s->rtpoll);
- pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
- s->thread_info.min_latency <= s->thread_info.max_latency);
+ pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
s->flags |= PA_SOURCE_DECIBEL_VOLUME;
@@ -315,6 +318,11 @@ void pa_source_put(pa_source *s) {
if (s->flags & PA_SOURCE_DECIBEL_VOLUME)
s->n_volume_steps = PA_VOLUME_NORM+1;
+ if (s->flags & PA_SOURCE_DYNAMIC_LATENCY)
+ s->fixed_latency = 0;
+ else if (s->fixed_latency <= 0)
+ s->fixed_latency = DEFAULT_FIXED_LATENCY;
+
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
@@ -465,8 +473,12 @@ pa_queue *pa_source_move_all_start(pa_source *s) {
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
+ pa_source_output_ref(o);
+
if (pa_source_output_start_move(o) >= 0)
- pa_queue_push(q, pa_source_output_ref(o));
+ pa_queue_push(q, o);
+ else
+ pa_source_output_unref(o);
}
return q;
@@ -616,6 +628,32 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
return usec;
}
+/* Called from IO thread */
+pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
+ pa_usec_t usec = 0;
+ pa_msgobject *o;
+
+ pa_source_assert_ref(s);
+ pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+ /* The returned value is supposed to be in the time domain of the sound card! */
+
+ if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+ return 0;
+
+ if (!(s->flags & PA_SOURCE_LATENCY))
+ return 0;
+
+ o = PA_MSGOBJECT(s);
+
+ /* We probably should make this a proper vtable callback instead of going through process_msg() */
+
+ if (o->process_msg(o, PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+ return -1;
+
+ return usec;
+}
+
/* Called from main thread */
void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
pa_cvolume old_virtual_volume;
@@ -675,6 +713,19 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) {
+ pa_source_assert_ref(s);
+
+ /* The source implementor may call this if the volume changed to make sure everyone is notified */
+
+ if (pa_cvolume_equal(&s->virtual_volume, new_volume))
+ return;
+
+ s->virtual_volume = *new_volume;
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
pa_bool_t old_muted;
@@ -695,7 +746,6 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
/* Called from main thread */
pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
-
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
@@ -715,11 +765,24 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
+void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted) {
+ pa_source_assert_ref(s);
+
+ /* The source implementor may call this if the mute state changed to make sure everyone is notified */
+
+ if (s->muted == new_muted)
+ return;
+
+ s->muted = new_muted;
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from main thread */
pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) {
pa_source_assert_ref(s);
- pa_assert(p);
- pa_proplist_update(s->proplist, mode, p);
+ if (p)
+ pa_proplist_update(s->proplist, mode, p);
if (PA_SOURCE_IS_LINKED(s->state)) {
pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
@@ -881,9 +944,26 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
case PA_SOURCE_MESSAGE_GET_MUTE:
return 0;
- case PA_SOURCE_MESSAGE_SET_STATE:
+ case PA_SOURCE_MESSAGE_SET_STATE: {
+
+ pa_bool_t suspend_change =
+ (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+ (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
+
s->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+ if (suspend_change) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->suspend_within_thread)
+ o->suspend_within_thread(o, s->thread_info.state == PA_SOURCE_SUSPENDED);
+ }
+
+
return 0;
+ }
case PA_SOURCE_MESSAGE_DETACH:
@@ -911,7 +991,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
case PA_SOURCE_MESSAGE_SET_LATENCY_RANGE: {
pa_usec_t *r = userdata;
- pa_source_update_latency_range(s, r[0], r[1]);
+ pa_source_set_latency_range_within_thread(s, r[0], r[1]);
return 0;
}
@@ -930,6 +1010,11 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
*((size_t*) userdata) = s->thread_info.max_rewind;
return 0;
+ case PA_SOURCE_MESSAGE_SET_MAX_REWIND:
+
+ pa_source_set_max_rewind_within_thread(s, (size_t) offset);
+ return 0;
+
case PA_SOURCE_MESSAGE_GET_LATENCY:
if (s->monitor_of) {
@@ -1018,6 +1103,9 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
pa_source_assert_ref(s);
+ if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
if (s->thread_info.requested_latency_valid)
return s->thread_info.requested_latency;
@@ -1027,13 +1115,8 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
(result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
result = o->thread_info.requested_source_latency;
- if (result != (pa_usec_t) -1) {
- if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
- result = s->thread_info.max_latency;
-
- if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
- result = s->thread_info.min_latency;
- }
+ if (result != (pa_usec_t) -1)
+ result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
s->thread_info.requested_latency = result;
s->thread_info.requested_latency_valid = TRUE;
@@ -1043,7 +1126,7 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
/* Called from main thread */
pa_usec_t pa_source_get_requested_latency(pa_source *s) {
- pa_usec_t usec;
+ pa_usec_t usec = 0;
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
@@ -1057,7 +1140,7 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) {
}
/* Called from IO thread */
-void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind) {
pa_source_output *o;
void *state = NULL;
@@ -1074,42 +1157,64 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
}
}
+/* Called from main thread */
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) {
+ pa_source_assert_ref(s);
+
+ if (PA_SOURCE_IS_LINKED(s->state))
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MAX_REWIND, NULL, max_rewind, NULL) == 0);
+ else
+ pa_source_set_max_rewind_within_thread(s, max_rewind);
+}
+
+/* Called from IO thread */
void pa_source_invalidate_requested_latency(pa_source *s) {
pa_source_output *o;
void *state = NULL;
pa_source_assert_ref(s);
+ if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ return;
+
s->thread_info.requested_latency_valid = FALSE;
- if (s->update_requested_latency)
- s->update_requested_latency(s);
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
- if (o->update_source_requested_latency)
- o->update_source_requested_latency(o);
+ if (s->update_requested_latency)
+ s->update_requested_latency(s);
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->update_source_requested_latency)
+ o->update_source_requested_latency(o);
+ }
if (s->monitor_of)
pa_sink_invalidate_requested_latency(s->monitor_of);
}
+/* Called from main thread */
void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
pa_source_assert_ref(s);
/* min_latency == 0: no limit
- * min_latency == (size_t) -1: default limit
* min_latency anything else: specified limit
*
* Similar for max_latency */
- if (min_latency == (pa_usec_t) -1)
- min_latency = DEFAULT_MIN_LATENCY;
+ if (min_latency < ABSOLUTE_MIN_LATENCY)
+ min_latency = ABSOLUTE_MIN_LATENCY;
- if (max_latency == (pa_usec_t) -1)
- max_latency = min_latency;
+ if (max_latency <= 0 ||
+ max_latency > ABSOLUTE_MAX_LATENCY)
+ max_latency = ABSOLUTE_MAX_LATENCY;
- pa_assert(!min_latency || !max_latency ||
- min_latency <= max_latency);
+ pa_assert(min_latency <= max_latency);
+
+ /* Hmm, let's see if someone forgot to set PA_SOURCE_DYNAMIC_LATENCY here... */
+ pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+ max_latency == ABSOLUTE_MAX_LATENCY) ||
+ (s->flags & PA_SOURCE_DYNAMIC_LATENCY));
if (PA_SOURCE_IS_LINKED(s->state)) {
pa_usec_t r[2];
@@ -1118,14 +1223,11 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t
r[1] = max_latency;
pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_LATENCY_RANGE, r, 0, NULL) == 0);
- } else {
- s->thread_info.min_latency = min_latency;
- s->thread_info.max_latency = max_latency;
-
- s->thread_info.requested_latency_valid = FALSE;
- }
+ } else
+ pa_source_set_latency_range_within_thread(s, min_latency, max_latency);
}
+/* Called from main thread */
void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t *max_latency) {
pa_source_assert_ref(s);
pa_assert(min_latency);
@@ -1144,26 +1246,37 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
}
}
-/* Called from IO thread */
-void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
- pa_source_output *o;
+/* Called from IO thread, and from main thread before pa_source_put() is called */
+void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
void *state = NULL;
pa_source_assert_ref(s);
- pa_assert(!min_latency || !max_latency ||
- min_latency <= max_latency);
+ pa_assert(min_latency >= ABSOLUTE_MIN_LATENCY);
+ pa_assert(max_latency <= ABSOLUTE_MAX_LATENCY);
+ pa_assert(min_latency <= max_latency);
+
+ /* Hmm, let's see if someone forgot to set PA_SOURCE_DYNAMIC_LATENCY here... */
+ pa_assert((min_latency == ABSOLUTE_MIN_LATENCY &&
+ max_latency == ABSOLUTE_MAX_LATENCY) ||
+ (s->flags & PA_SOURCE_DYNAMIC_LATENCY) ||
+ s->monitor_of);
s->thread_info.min_latency = min_latency;
s->thread_info.max_latency = max_latency;
- while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
- if (o->update_source_latency_range)
- o->update_source_latency_range(o);
+ if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
+ pa_source_output *o;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->update_source_latency_range)
+ o->update_source_latency_range(o);
+ }
pa_source_invalidate_requested_latency(s);
}
+/* Called from main thread */
size_t pa_source_get_max_rewind(pa_source *s) {
size_t r;
pa_source_assert_ref(s);
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 26471de..b502c22 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -87,6 +87,8 @@ struct pa_source {
pa_memchunk silence;
+ pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -159,6 +161,7 @@ typedef enum pa_source_message {
PA_SOURCE_MESSAGE_SET_LATENCY_RANGE,
PA_SOURCE_MESSAGE_GET_LATENCY_RANGE,
PA_SOURCE_MESSAGE_GET_MAX_REWIND,
+ PA_SOURCE_MESSAGE_SET_MAX_REWIND,
PA_SOURCE_MESSAGE_MAX
} pa_source_message_t;
@@ -205,12 +208,15 @@ void pa_source_set_description(pa_source *s, const char *description);
void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q);
void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p);
+void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume);
+void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted);
int pa_source_sync_suspend(pa_source *s);
@@ -257,11 +263,12 @@ void pa_source_detach_within_thread(pa_source *s);
pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
-void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
-void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
+void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind);
+void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
/*** To be called exclusively by source output drivers, from IO context */
void pa_source_invalidate_requested_latency(pa_source *s);
+pa_usec_t pa_source_get_latency_within_thread(pa_source *s);
#endif
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 6562194..9d5a070 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -78,17 +78,26 @@ struct pa_smoother {
/* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */
double a, b, c;
- pa_bool_t abc_valid;
+ pa_bool_t abc_valid:1;
pa_bool_t monotonic:1;
pa_bool_t paused:1;
+ pa_bool_t smoothing:1; /* If FALSE we skip the polonyomial interpolation step */
pa_usec_t pause_time;
unsigned min_history;
};
-pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) {
+pa_smoother* pa_smoother_new(
+ pa_usec_t adjust_time,
+ pa_usec_t history_time,
+ pa_bool_t monotonic,
+ pa_bool_t smoothing,
+ unsigned min_history,
+ pa_usec_t time_offset,
+ pa_bool_t paused) {
+
pa_smoother *s;
pa_assert(adjust_time > 0);
@@ -116,9 +125,13 @@ pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_b
s->abc_valid = FALSE;
s->paused = FALSE;
+ s->smoothing = smoothing;
s->min_history = min_history;
+ s->paused = paused;
+ s->time_offset = s->pause_time = time_offset;
+
return s;
}
@@ -279,6 +292,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
pa_assert(y);
if (x >= s->px) {
+ /* Linear interpolation right from px */
int64_t t;
/* The requested point is right of the point where we wanted
@@ -294,7 +308,22 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
if (deriv)
*deriv = s->dp;
+ } else if (x <= s->ex) {
+ /* Linear interpolation left from ex */
+ int64_t t;
+
+ t = (int64_t) s->ey - (int64_t) llrint(s->de * (double) (s->ex - x));
+
+ if (t < 0)
+ t = 0;
+
+ *y = (pa_usec_t) t;
+
+ if (deriv)
+ *deriv = s->de;
+
} else {
+ /* Spline interpolation between ex and px */
double tx, ty;
/* Ok, we're not yet on track, thus let's interpolate, and
@@ -348,7 +377,6 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
* we can adjust our position smoothly from this one */
estimate(s, x, &ney, &nde);
s->ex = x; s->ey = ney; s->de = nde;
-
s->ry = y;
}
@@ -359,12 +387,19 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
s->dp = avg_gradient(s, x);
/* And calculate when we want to be on track again */
- s->px = s->ex + s->adjust_time;
- s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
+ if (s->smoothing) {
+ s->px = s->ex + s->adjust_time;
+ s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
+ } else {
+ s->px = s->ex;
+ s->py = s->ry;
+ }
s->abc_valid = FALSE;
-/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+#ifdef DEBUG_DATA
+ pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
}
pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
@@ -395,7 +430,9 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
s->last_y = y;
}
-/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+#ifdef DEBUG_DATA
+ pa_log_debug("%p, get(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
return y;
}
@@ -405,7 +442,9 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
s->time_offset = offset;
-/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */
+#ifdef DEBUG_DATA
+ pa_log_debug("offset(%llu)", (unsigned long long) offset);
+#endif
}
void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
@@ -414,13 +453,15 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
if (s->paused)
return;
-/* pa_log_debug("pause(%llu)", (unsigned long long) x); */
+#ifdef DEBUG_DATA
+ pa_log_debug("pause(%llu)", (unsigned long long) x);
+#endif
s->paused = TRUE;
s->pause_time = x;
}
-void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t fix_now) {
pa_assert(s);
if (!s->paused)
@@ -429,10 +470,22 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) {
if (x < s->pause_time)
x = s->pause_time;
-/* pa_log_debug("resume(%llu)", (unsigned long long) x); */
+#ifdef DEBUG_DATA
+ pa_log_debug("resume(%llu)", (unsigned long long) x);
+#endif
s->paused = FALSE;
s->time_offset += x - s->pause_time;
+
+ if (fix_now)
+ pa_smoother_fix_now(s);
+}
+
+void pa_smoother_fix_now(pa_smoother *s) {
+ pa_assert(s);
+
+ s->px = s->ex;
+ s->py = s->ry;
}
pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) {
@@ -454,7 +507,9 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay)
if (s->dp > nde)
nde = s->dp;
-/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
+#ifdef DEBUG_DATA
+ pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde);
+#endif
return (pa_usec_t) llrint((double) y_delay / nde);
}
diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h
index 2051e64..5244a7e 100644
--- a/src/pulsecore/time-smoother.h
+++ b/src/pulsecore/time-smoother.h
@@ -27,7 +27,15 @@
typedef struct pa_smoother pa_smoother;
-pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history);
+pa_smoother* pa_smoother_new(
+ pa_usec_t x_adjust_time,
+ pa_usec_t x_history_time,
+ pa_bool_t monotonic,
+ pa_bool_t smoothing,
+ unsigned min_history,
+ pa_usec_t x_offset,
+ pa_bool_t paused);
+
void pa_smoother_free(pa_smoother* s);
/* Adds a new value to our dataset. x = local/system time, y = remote time */
@@ -42,8 +50,10 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay);
void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset);
void pa_smoother_pause(pa_smoother *s, pa_usec_t x);
-void pa_smoother_resume(pa_smoother *s, pa_usec_t x);
+void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t abrupt);
void pa_smoother_reset(pa_smoother *s);
+void pa_smoother_fix_now(pa_smoother *s);
+
#endif