summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/client.c42
-rw-r--r--src/pulsecore/client.h12
-rw-r--r--src/pulsecore/conf-parser.c55
-rw-r--r--src/pulsecore/conf-parser.h15
-rw-r--r--src/pulsecore/core-util.c51
-rw-r--r--src/pulsecore/core-util.h4
-rw-r--r--src/pulsecore/core.h4
-rw-r--r--src/pulsecore/envelope.c7
-rw-r--r--src/pulsecore/modargs.c179
-rw-r--r--src/pulsecore/native-common.h4
-rw-r--r--src/pulsecore/play-memblockq.c2
-rw-r--r--src/pulsecore/proplist-util.c25
-rw-r--r--src/pulsecore/protocol-native.c89
-rw-r--r--src/pulsecore/resampler.c12
-rw-r--r--src/pulsecore/sink-input.c137
-rw-r--r--src/pulsecore/sink-input.h26
-rw-r--r--src/pulsecore/sink.c1
-rw-r--r--src/pulsecore/sound-file-stream.c2
-rw-r--r--src/pulsecore/source-output.c52
-rw-r--r--src/pulsecore/source-output.h14
20 files changed, 516 insertions, 217 deletions
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index 18004412..7ca7b97c 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -73,6 +73,7 @@ pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) {
c->userdata = NULL;
c->kill = NULL;
+ c->send_event = NULL;
pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
@@ -126,8 +127,49 @@ void pa_client_kill(pa_client *c) {
void pa_client_set_name(pa_client *c, const char *name) {
pa_assert(c);
+ pa_assert(name);
pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name);
pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+
+ pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c);
+ pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+}
+
+void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p) {
+ pa_assert(c);
+ pa_assert(p);
+
+ pa_proplist_update(c->proplist, mode, p);
+
+ pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
}
+
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data) {
+ pa_proplist *pl = NULL;
+ pa_client_send_event_hook_data hook_data;
+
+ pa_assert(c);
+ pa_assert(event);
+
+ if (!c->send_event)
+ return;
+
+ if (!data)
+ data = pl = pa_proplist_new();
+
+ hook_data.client = c;
+ hook_data.data = data;
+ hook_data.event = event;
+
+ if (pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_SEND_EVENT], &hook_data) < 0)
+ goto finish;
+
+ c->send_event(c, event, data);
+
+finish:
+
+ if (pl)
+ pa_proplist_free(pl);
+}
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
index 48e9bc7a..845a8bab 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -48,6 +48,8 @@ struct pa_client {
void *userdata;
void (*kill)(pa_client *c);
+
+ void (*send_event)(pa_client *c, const char *name, pa_proplist *data);
};
typedef struct pa_client_new_data {
@@ -71,4 +73,14 @@ void pa_client_kill(pa_client *c);
/* Rename the client */
void pa_client_set_name(pa_client *c, const char *name);
+void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p);
+
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data);
+
+typedef struct pa_client_send_event_hook_data {
+ pa_client *client;
+ const char *event;
+ pa_proplist *data;
+} pa_client_send_event_hook_data;
+
#endif
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index ef6d6bb6..b7ec2b3c 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -40,17 +40,19 @@
#define COMMENTS "#;\n"
/* Run the user supplied parser for an assignment */
-static int next_assignment(const char *filename, unsigned line, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
+static int next_assignment(const char *filename, unsigned line, const char *section, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
pa_assert(filename);
pa_assert(t);
pa_assert(lvalue);
pa_assert(rvalue);
for (; t->parse; t++)
- if (!strcmp(lvalue, t->lvalue))
- return t->parse(filename, line, lvalue, rvalue, t->data, userdata);
+ if (!t->lvalue ||
+ (pa_streq(lvalue, t->lvalue) &&
+ ((!section && !t->section) || pa_streq(section, t->section))))
+ return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
- pa_log("[%s:%u] Unknown lvalue '%s'.", filename, line, lvalue);
+ pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strnull(section));
return -1;
}
@@ -83,8 +85,10 @@ static char *strip(char *s) {
}
/* Parse a variable assignment line */
-static int parse_line(const char *filename, unsigned line, const pa_config_item *t, char *l, void *userdata) {
- char *e, *c, *b = l+strspn(l, WHITESPACE);
+static int parse_line(const char *filename, unsigned line, char **section, const pa_config_item *t, char *l, void *userdata) {
+ char *e, *c, *b;
+
+ b = l+strspn(l, WHITESPACE);
if ((c = strpbrk(b, COMMENTS)))
*c = 0;
@@ -92,6 +96,22 @@ static int parse_line(const char *filename, unsigned line, const pa_config_item
if (!*b)
return 0;
+ if (*b == '[') {
+ size_t k;
+
+ k = strlen(b);
+ pa_assert(k > 0);
+
+ if (b[k-1] != ']') {
+ pa_log("[%s:%u] Invalid section header.", filename, line);
+ return -1;
+ }
+
+ pa_xfree(*section);
+ *section = pa_xstrndup(b+1, k-2);
+ return 0;
+ }
+
if (!(e = strchr(b, '='))) {
pa_log("[%s:%u] Missing '='.", filename, line);
return -1;
@@ -100,7 +120,7 @@ static int parse_line(const char *filename, unsigned line, const pa_config_item
*e = 0;
e++;
- return next_assignment(filename, line, t, strip(b), strip(e), userdata);
+ return next_assignment(filename, line, *section, t, strip(b), strip(e), userdata);
}
/* Go through the file and parse each line */
@@ -108,6 +128,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
int r = -1;
unsigned line = 0;
int do_close = !f;
+ char *section = NULL;
pa_assert(filename);
pa_assert(t);
@@ -118,29 +139,29 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
goto finish;
}
- pa_log_warn("Failed to open configuration file '%s': %s",
- filename, pa_cstrerror(errno));
+ pa_log_warn("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
goto finish;
}
while (!feof(f)) {
char l[256];
+
if (!fgets(l, sizeof(l), f)) {
if (feof(f))
break;
- pa_log_warn("Failed to read configuration file '%s': %s",
- filename, pa_cstrerror(errno));
+ pa_log_warn("Failed to read configuration file '%s': %s", filename, pa_cstrerror(errno));
goto finish;
}
- if (parse_line(filename, ++line, t, l, userdata) < 0)
+ if (parse_line(filename, ++line, &section, t, l, userdata) < 0)
goto finish;
}
r = 0;
finish:
+ pa_xfree(section);
if (do_close && f)
fclose(f);
@@ -148,7 +169,7 @@ finish:
return r;
}
-int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+int pa_config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
int *i = data;
int32_t k;
@@ -166,7 +187,7 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue,
return 0;
}
-int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+int pa_config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
unsigned *u = data;
uint32_t k;
@@ -184,7 +205,7 @@ int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lv
return 0;
}
-int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+int pa_config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
size_t *i = data;
uint32_t k;
@@ -202,7 +223,7 @@ int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue
return 0;
}
-int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+int pa_config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
int k;
pa_bool_t *b = data;
@@ -221,7 +242,7 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue
return 0;
}
-int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
char **s = data;
pa_assert(filename);
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
index 48a0fd26..f8f059fe 100644
--- a/src/pulsecore/conf-parser.h
+++ b/src/pulsecore/conf-parser.h
@@ -27,11 +27,14 @@
/* An abstract parser for simple, line based, shallow configuration
* files consisting of variable assignments only. */
+typedef int (*pa_config_parser_cb_t)(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
+
/* Wraps info for parsing a specific configuration variable */
typedef struct pa_config_item {
const char *lvalue; /* name of the variable */
- int (*parse)(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata); /* Function that is called to parse the variable's value */
+ pa_config_parser_cb_t parse; /* Function that is called to parse the variable's value */
void *data; /* Where to store the variable's data */
+ const char *section;
} pa_config_item;
/* The configuration file parsing routine. Expects a table of
@@ -40,10 +43,10 @@ typedef struct pa_config_item {
int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void *userdata);
/* Generic parsers for integers, size_t, booleans and strings */
-int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
-int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
-int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
-int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
-int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
#endif
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index e65b1796..ad6c6ca9 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -88,6 +88,10 @@
#include <samplerate.h>
#endif
+#ifdef __APPLE__
+#include <xlocale.h>
+#endif
+
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/utf8.h>
@@ -97,6 +101,7 @@
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
#include <pulsecore/thread.h>
+#include <pulsecore/strbuf.h>
#include "core-util.h"
@@ -2553,3 +2558,49 @@ unsigned pa_ncpus(void) {
return ncpus <= 0 ? 1 : (unsigned) ncpus;
}
+
+char *pa_replace(const char*s, const char*a, const char *b) {
+ pa_strbuf *sb;
+ size_t an;
+
+ pa_assert(s);
+ pa_assert(a);
+ pa_assert(b);
+
+ an = strlen(a);
+ sb = pa_strbuf_new();
+
+ for (;;) {
+ const char *p;
+
+ if (!(p = strstr(s, a)))
+ break;
+
+ pa_strbuf_putsn(sb, s, p-s);
+ pa_strbuf_puts(sb, b);
+ s = p + an;
+ }
+
+ pa_strbuf_puts(sb, s);
+
+ return pa_strbuf_tostring_free(sb);
+}
+
+char *pa_unescape(char *p) {
+ char *s, *d;
+ pa_bool_t escaped = FALSE;
+
+ for (s = p, d = p; *s; s++) {
+ if (!escaped && *s == '\\') {
+ escaped = TRUE;
+ continue;
+ }
+
+ *(d++) = *s;
+ escaped = FALSE;
+ }
+
+ *d = 0;
+
+ return p;
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 18901f47..442815f1 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -215,4 +215,8 @@ void pa_reduce(unsigned *num, unsigned *den);
unsigned pa_ncpus(void);
+char *pa_replace(const char*s, const char*a, const char *b);
+
+char *pa_unescape(char *p);
+
#endif
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 8fa9761f..53e2d2c3 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -74,6 +74,7 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
+ PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
@@ -84,9 +85,12 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL,
PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
PA_CORE_HOOK_CLIENT_NEW,
PA_CORE_HOOK_CLIENT_PUT,
PA_CORE_HOOK_CLIENT_UNLINK,
+ PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED,
+ PA_CORE_HOOK_CLIENT_SEND_EVENT,
PA_CORE_HOOK_CARD_NEW,
PA_CORE_HOOK_CARD_PUT,
PA_CORE_HOOK_CARD_UNLINK,
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
index b57b6435..fd6a9487 100644
--- a/src/pulsecore/envelope.c
+++ b/src/pulsecore/envelope.c
@@ -744,6 +744,13 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
break;
}
+ case PA_SAMPLE_S24LE:
+ case PA_SAMPLE_S24BE:
+ case PA_SAMPLE_S24_32LE:
+ case PA_SAMPLE_S24_32BE:
+ /* FIXME */
+ pa_assert_not_reached();
+
case PA_SAMPLE_MAX:
case PA_SAMPLE_INVALID:
pa_assert_not_reached();
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 9e60125e..866e6e0c 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -79,106 +79,111 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co
}
pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) {
- pa_hashmap *map = NULL;
+ enum {
+ WHITESPACE,
+ KEY,
+ VALUE_START,
+ VALUE_SIMPLE,
+ VALUE_DOUBLE_QUOTES,
+ VALUE_TICKS
+ } state;
+
+ const char *p, *key = NULL, *value = NULL;
+ size_t key_len = 0, value_len = 0;
+ pa_hashmap *map;
map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- if (args) {
- enum {
- WHITESPACE,
- KEY,
- VALUE_START,
- VALUE_SIMPLE,
- VALUE_DOUBLE_QUOTES,
- VALUE_TICKS
- } state;
-
- const char *p, *key, *value;
- size_t key_len = 0, value_len = 0;
-
- key = value = NULL;
- state = WHITESPACE;
- for (p = args; *p; p++) {
- switch (state) {
- case WHITESPACE:
- if (*p == '=')
+ if (!args)
+ return (pa_modargs*) map;
+
+ state = WHITESPACE;
+
+ for (p = args; *p; p++) {
+ switch (state) {
+
+ case WHITESPACE:
+ if (*p == '=')
+ goto fail;
+ else if (!isspace(*p)) {
+ key = p;
+ state = KEY;
+ key_len = 1;
+ }
+ break;
+
+ case KEY:
+ if (*p == '=')
+ state = VALUE_START;
+ else if (isspace(*p))
+ goto fail;
+ else
+ key_len++;
+ break;
+
+ case VALUE_START:
+ if (*p == '\'') {
+ state = VALUE_TICKS;
+ value = p+1;
+ value_len = 0;
+ } else if (*p == '"') {
+ state = VALUE_DOUBLE_QUOTES;
+ value = p+1;
+ value_len = 0;
+ } else if (isspace(*p)) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
goto fail;
- else if (!isspace(*p)) {
- key = p;
- state = KEY;
- key_len = 1;
- }
- break;
- case KEY:
- if (*p == '=')
- state = VALUE_START;
- else if (isspace(*p))
+ state = WHITESPACE;
+ } else {
+ state = VALUE_SIMPLE;
+ value = p;
+ value_len = 1;
+ }
+ break;
+
+ case VALUE_SIMPLE:
+ if (isspace(*p)) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
goto fail;
- else
- key_len++;
- break;
- case VALUE_START:
- if (*p == '\'') {
- state = VALUE_TICKS;
- value = p+1;
- value_len = 0;
- } else if (*p == '"') {
- state = VALUE_DOUBLE_QUOTES;
- value = p+1;
- value_len = 0;
- } else if (isspace(*p)) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
- goto fail;
- state = WHITESPACE;
- } else {
- state = VALUE_SIMPLE;
- value = p;
- value_len = 1;
- }
- break;
- case VALUE_SIMPLE:
- if (isspace(*p)) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
- goto fail;
- state = WHITESPACE;
- } else
- value_len++;
- break;
- case VALUE_DOUBLE_QUOTES:
- if (*p == '"') {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
- goto fail;
- state = WHITESPACE;
- } else
- value_len++;
- break;
- case VALUE_TICKS:
- if (*p == '\'') {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
- goto fail;
- state = WHITESPACE;
- } else
- value_len++;
- break;
- }
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+
+ case VALUE_DOUBLE_QUOTES:
+ if (*p == '"') {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
+
+ case VALUE_TICKS:
+ if (*p == '\'') {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0)
+ goto fail;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
}
+ }
- if (state == VALUE_START) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
- goto fail;
- } else if (state == VALUE_SIMPLE) {
- if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0)
- goto fail;
- } else if (state != WHITESPACE)
+ if (state == VALUE_START) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0)
goto fail;
- }
+ } else if (state == VALUE_SIMPLE) {
+ if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0)
+ goto fail;
+ } else if (state != WHITESPACE)
+ goto fail;
return (pa_modargs*) map;
fail:
- if (map)
- pa_modargs_free((pa_modargs*) map);
+ pa_modargs_free((pa_modargs*) map);
return NULL;
}
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index b31a5da1..6951e10a 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -157,6 +157,10 @@ enum {
PA_COMMAND_GET_CARD_INFO_LIST,
PA_COMMAND_SET_CARD_PROFILE,
+ PA_COMMAND_CLIENT_EVENT,
+ PA_COMMAND_PLAYBACK_STREAM_EVENT,
+ PA_COMMAND_RECORD_STREAM_EVENT,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index d1631264..44aa6bf0 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -197,7 +197,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
- pa_sink_input_new_data_set_virtual_volume(&data, volume);
+ pa_sink_input_new_data_set_volume(&data, volume);
pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index 4920c27e..8a447cf7 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -27,6 +27,13 @@
#include <locale.h>
#include <dlfcn.h>
+#ifdef __APPLE__
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
+#elif !HAVE_DECL_ENVIRON
+extern char **environ;
+#endif
+
#include <pulse/proplist.h>
#include <pulse/utf8.h>
#include <pulse/xmalloc.h>
@@ -37,10 +44,8 @@
#include "proplist-util.h"
void pa_init_proplist(pa_proplist *p) {
-#if !HAVE_DECL_ENVIRON
- extern char **environ;
-#endif
char **e;
+ const char *pp;
pa_assert(p);
@@ -64,17 +69,21 @@ void pa_init_proplist(pa_proplist *p) {
k = pa_xstrndup(*e+11, kl);
- if (pa_proplist_contains(p, k)) {
- pa_xfree(k);
- continue;
- }
-
pa_proplist_sets(p, k, *e+11+kl+1);
pa_xfree(k);
}
}
}
+ if ((pp = getenv("PULSE_PROP"))) {
+ pa_proplist *t;
+
+ if ((t = pa_proplist_from_string(pp))) {
+ pa_proplist_update(p, PA_UPDATE_REPLACE, t);
+ pa_proplist_free(t);
+ }
+ }
+
if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
char t[32];
pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 21a2cfbc..c3032618 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -207,6 +207,7 @@ static void sink_input_moved_cb(pa_sink_input *i);
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);
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
static void native_connection_send_memblock(pa_native_connection *c);
static void playback_stream_request_bytes(struct playback_stream*s);
@@ -216,6 +217,7 @@ 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 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);
@@ -595,7 +597,7 @@ static record_stream* record_stream_new(
int *ret) {
record_stream *s;
- pa_source_output *source_output;
+ pa_source_output *source_output = NULL;
size_t base;
pa_source_output_new_data data;
@@ -618,7 +620,7 @@ static record_stream* record_stream_new(
if (peak_detect)
data.resample_method = PA_RESAMPLER_PEAKS;
- *ret = pa_source_output_new(&source_output, c->protocol->core, &data, flags);
+ *ret = -pa_source_output_new(&source_output, c->protocol->core, &data, flags);
pa_source_output_new_data_done(&data);
@@ -636,6 +638,7 @@ static record_stream* record_stream_new(
s->source_output->get_latency = source_output_get_latency_cb;
s->source_output->moved = source_output_moved_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);
@@ -971,7 +974,7 @@ static playback_stream* playback_stream_new(
int *ret) {
playback_stream *s, *ssync;
- pa_sink_input *sink_input;
+ pa_sink_input *sink_input = NULL;
pa_memchunk silence;
uint32_t idx;
int64_t start_index;
@@ -1018,12 +1021,12 @@ static playback_stream* playback_stream_new(
pa_sink_input_new_data_set_sample_spec(&data, ss);
pa_sink_input_new_data_set_channel_map(&data, map);
if (volume)
- pa_sink_input_new_data_set_virtual_volume(&data, volume);
+ pa_sink_input_new_data_set_volume(&data, volume);
if (muted_set)
pa_sink_input_new_data_set_muted(&data, muted);
data.sync_base = ssync ? ssync->sink_input : NULL;
- *ret = pa_sink_input_new(&sink_input, c->protocol->core, &data, flags);
+ *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data, flags);
pa_sink_input_new_data_done(&data);
@@ -1048,6 +1051,7 @@ static playback_stream* playback_stream_new(
s->sink_input->kill = sink_input_kill_cb;
s->sink_input->moved = sink_input_moved_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;
@@ -1494,6 +1498,27 @@ static void sink_input_kill_cb(pa_sink_input *i) {
}
/* Called from main context */
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
+ playback_stream *s;
+ pa_tagstruct *t;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ if (s->connection->version < 15)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_puts(t, event);
+ pa_tagstruct_put_proplist(t, pl);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
playback_stream *s;
pa_tagstruct *t;
@@ -1595,6 +1620,27 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
}
/* Called from main context */
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
+ record_stream *s;
+ pa_tagstruct *t;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ if (s->connection->version < 15)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_puts(t, event);
+ pa_tagstruct_put_proplist(t, pl);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
record_stream *s;
pa_tagstruct *t;
@@ -2295,11 +2341,9 @@ static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t
return;
}
- pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p);
+ pa_client_update_proplist(c->client, PA_UPDATE_REPLACE, p);
pa_proplist_free(p);
- pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
-
reply = reply_new(tag);
if (c->version >= 13)
@@ -3551,8 +3595,7 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- pa_proplist_update(s->sink_input->proplist, mode, p);
- pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+ pa_sink_input_update_proplist(s->sink_input, mode, p);
} else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
record_stream *s;
@@ -3560,13 +3603,11 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- pa_proplist_update(s->source_output->proplist, mode, p);
- pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
+ pa_source_output_update_proplist(s->source_output, mode, p);
} else {
pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
- pa_proplist_update(c->client->proplist, mode, p);
- pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+ pa_client_update_proplist(c->client, mode, p);
}
pa_pstream_send_simple_ack(c->pstream, tag);
@@ -4193,6 +4234,25 @@ static void client_kill_cb(pa_client *c) {
pa_log_info("Connection killed.");
}
+static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
+ pa_tagstruct *t;
+ pa_native_connection *c;
+
+ pa_assert(client);
+ c = PA_NATIVE_CONNECTION(client->userdata);
+ pa_native_connection_assert_ref(c);
+
+ if (c->version < 15)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_puts(t, event);
+ pa_tagstruct_put_proplist(t, pl);
+ pa_pstream_send_tagstruct(c->pstream, t);
+}
+
/*** module entry points ***/
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
@@ -4270,6 +4330,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
c->client = client;
c->client->kill = client_kill_cb;
+ c->client->send_event = client_send_event_cb;
c->client->userdata = c;
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index be390db7..78ad5530 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -156,16 +156,6 @@ static int (* const init_table[])(pa_resampler*r) = {
[PA_RESAMPLER_PEAKS] = peaks_init,
};
-static inline size_t sample_size(pa_sample_format_t f) {
- pa_sample_spec ss = {
- .format = f,
- .rate = 0,
- .channels = 1
- };
-
- return pa_sample_size(&ss);
-}
-
pa_resampler* pa_resampler_new(
pa_mempool *pool,
const pa_sample_spec *a,
@@ -275,7 +265,7 @@ pa_resampler* pa_resampler_new(
pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format));
- r->w_sz = sample_size(r->work_format);
+ r->w_sz = pa_sample_size_of_format(r->work_format);
if (r->i_ss.format == r->work_format)
r->to_work_format_func = NULL;
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index fc87d5d8..22419ee5 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -72,18 +72,23 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const
data->channel_map = *map;
}
-void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
pa_assert(data);
- if ((data->soft_volume_is_set = !!volume))
- data->soft_volume = *volume;
+ if ((data->volume_is_set = !!volume))
+ data->volume = *volume;
}
-void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor) {
pa_assert(data);
+ pa_assert(volume_factor);
- if ((data->virtual_volume_is_set = !!volume))
- data->virtual_volume = *volume;
+ if (data->volume_factor_is_set)
+ pa_sw_cvolume_multiply(&data->volume_factor, &data->volume_factor, volume_factor);
+ else {
+ data->volume_factor_is_set = TRUE;
+ data->volume_factor = *volume_factor;
+ }
}
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
@@ -117,6 +122,7 @@ static void reset_callbacks(pa_sink_input *i) {
i->get_latency = NULL;
i->state_change = NULL;
i->may_move_to = NULL;
+ i->send_event = NULL;
}
/* Called from main context */
@@ -136,6 +142,9 @@ int pa_sink_input_new(
pa_assert(core);
pa_assert(data);
+ if (data->client)
+ pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
+
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
return r;
@@ -166,35 +175,28 @@ int pa_sink_input_new(
pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
- if (!data->virtual_volume_is_set) {
+ if (!data->volume_is_set) {
if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
- data->virtual_volume = data->sink->virtual_volume;
- pa_cvolume_remap(&data->virtual_volume, &data->sink->channel_map, &data->channel_map);
- } else
- pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
+ 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;
+ }
data->save_volume = FALSE;
-
- } else if (!data->virtual_volume_is_absolute) {
-
- /* When the 'absolute' bool is set then we'll treat the volume
- * as relative to the sink volume even in flat volume mode */
- if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume t = data->sink->virtual_volume;
- pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map);
- pa_sw_cvolume_multiply(&data->virtual_volume, &data->virtual_volume, &t);
- }
}
- pa_return_val_if_fail(pa_cvolume_valid(&data->virtual_volume), -PA_ERR_INVALID);
- pa_return_val_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec), -PA_ERR_INVALID);
+ pa_return_val_if_fail(pa_cvolume_valid(&data->volume), -PA_ERR_INVALID);
+ pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
- if (!data->soft_volume_is_set)
- data->soft_volume = data->virtual_volume;
+ if (!data->volume_factor_is_set)
+ pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
- pa_return_val_if_fail(pa_cvolume_valid(&data->soft_volume), -PA_ERR_INVALID);
- pa_return_val_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec), -PA_ERR_INVALID);
+ pa_return_val_if_fail(pa_cvolume_valid(&data->volume_factor), -PA_ERR_INVALID);
+ pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
if (!data->muted_is_set)
data->muted = FALSE;
@@ -216,17 +218,13 @@ int pa_sink_input_new(
pa_assert(pa_channel_map_valid(&data->channel_map));
/* Due to the fixing of the sample spec the volume might not match anymore */
- pa_cvolume_remap(&data->soft_volume, &original_cm, &data->channel_map);
- pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map);
+ pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
- if (data->client)
- pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
-
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
return r;
@@ -271,8 +269,19 @@ int pa_sink_input_new(
i->sample_spec = data->sample_spec;
i->channel_map = data->channel_map;
- i->virtual_volume = data->virtual_volume;
- i->soft_volume = data->soft_volume;
+ if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) {
+ /* 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);
+ } else
+ i->virtual_volume = data->volume;
+
+ i->volume_factor = data->volume_factor;
+ pa_cvolume_init(&i->soft_volume);
i->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -502,9 +511,6 @@ void pa_sink_input_put(pa_sink_input *i) {
pa_assert(i->process_rewind);
pa_assert(i->kill);
- i->thread_info.soft_volume = i->soft_volume;
- i->thread_info.muted = i->muted;
-
state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
update_n_corked(i, state);
@@ -515,7 +521,11 @@ void pa_sink_input_put(pa_sink_input *i) {
pa_cvolume new_volume;
pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
- }
+ } else
+ pa_sw_cvolume_multiply(&i->soft_volume, &i->virtual_volume, &i->volume_factor);
+
+ i->thread_info.soft_volume = i->soft_volume;
+ i->thread_info.muted = i->muted;
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
@@ -886,8 +896,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
-
- i->soft_volume = *volume;
+ pa_sw_cvolume_multiply(&i->soft_volume, volume, &i->volume_factor);
/* 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);
@@ -932,18 +941,16 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
}
/* Called from main thread */
-pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
-
- pa_sink_input_assert_ref(i);
-
- pa_proplist_update(i->proplist, mode, p);
+void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(p);
- if (PA_SINK_IS_LINKED(i->state)) {
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
+ pa_proplist_update(i->proplist, mode, p);
- return TRUE;
+ if (PA_SINK_IS_LINKED(i->state)) {
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
}
/* Called from main context */
@@ -1078,7 +1085,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
/* Make the absolute volume relative */
i->virtual_volume = i->soft_volume;
- pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels);
+ i->soft_volume = i->volume_factor;
/* We might need to update the sink's volume if we are in flat
* volume mode. */
@@ -1440,3 +1447,31 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
return ret;
}
+
+/* Called from main context */
+void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *data) {
+ pa_proplist *pl = NULL;
+ pa_sink_input_send_event_hook_data hook_data;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(event);
+
+ if (!i->send_event)
+ return;
+
+ if (!data)
+ data = pl = pa_proplist_new();
+
+ hook_data.sink_input = i;
+ hook_data.data = data;
+ hook_data.event = event;
+
+ if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], &hook_data) < 0)
+ goto finish;
+
+ i->send_event(i, event, data);
+
+finish:
+ if (pl)
+ pa_proplist_free(pl);
+}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index d82a3a62..b4f05319 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -91,7 +91,7 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
- pa_cvolume virtual_volume, soft_volume;
+ pa_cvolume virtual_volume, soft_volume, volume_factor;
pa_bool_t muted:1;
/* if TRUE then the source we are connected to and/or the volume
@@ -171,6 +171,10 @@ struct pa_sink_input {
* be allowed */
pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */
+ /* If non-NULL this function is used to dispatch asynchronous
+ * control events. */
+ void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data);
+
struct {
pa_sink_input_state_t state;
pa_atomic_t drained;
@@ -217,6 +221,12 @@ enum {
PA_SINK_INPUT_MESSAGE_MAX
};
+typedef struct pa_sink_input_send_event_hook_data {
+ pa_sink_input *sink_input;
+ const char *event;
+ pa_proplist *data;
+} pa_sink_input_send_event_hook_data;
+
typedef struct pa_sink_input_new_data {
pa_proplist *proplist;
@@ -233,16 +243,16 @@ typedef struct pa_sink_input_new_data {
pa_sample_spec sample_spec;
pa_channel_map channel_map;
- pa_cvolume virtual_volume, soft_volume;
+ pa_cvolume volume, volume_factor;
pa_bool_t muted:1;
pa_bool_t sample_spec_is_set:1;
pa_bool_t channel_map_is_set:1;
- pa_bool_t virtual_volume_is_set:1, soft_volume_is_set:1;
+ pa_bool_t volume_is_set:1, volume_factor_is_set:1;
pa_bool_t muted_is_set:1;
- pa_bool_t virtual_volume_is_absolute:1;
+ pa_bool_t volume_is_absolute:1;
pa_bool_t save_sink:1, save_volume:1, save_muted:1;
} pa_sink_input_new_data;
@@ -250,8 +260,8 @@ typedef struct pa_sink_input_new_data {
pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
-void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
-void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
@@ -294,10 +304,12 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
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);
-pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
+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);
+void pa_sink_input_send_event(pa_sink_input *i, const char *name, pa_proplist *data);
+
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */
pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 558da8c6..eadef809 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -992,6 +992,7 @@ 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);
/* 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);
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 67b072b6..c3de3067 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -322,7 +322,7 @@ int pa_play_file(
data.sink = sink;
data.driver = __FILE__;
pa_sink_input_new_data_set_sample_spec(&data, &ss);
- pa_sink_input_new_data_set_virtual_volume(&data, volume);
+ pa_sink_input_new_data_set_volume(&data, volume);
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index c5cb16d8..382fb88c 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -92,6 +92,7 @@ static void reset_callbacks(pa_source_output *o) {
o->get_latency = NULL;
o->state_change = NULL;
o->may_move_to = NULL;
+ o->send_event = NULL;
}
/* Called from main context */
@@ -110,6 +111,9 @@ int pa_source_output_new(
pa_assert(core);
pa_assert(data);
+ if (data->client)
+ pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
+
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
return r;
@@ -159,9 +163,6 @@ int pa_source_output_new(
pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
- if (data->client)
- pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
-
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
return r;
@@ -615,18 +616,16 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
}
/* Called from main thread */
-pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
-
- pa_source_output_assert_ref(o);
-
- pa_proplist_update(o->proplist, mode, p);
+void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
+ pa_source_output_assert_ref(o);
+ pa_assert(p);
- if (PA_SINK_IS_LINKED(o->state)) {
- pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- }
+ pa_proplist_update(o->proplist, mode, p);
- return TRUE;
+ if (PA_SINK_IS_LINKED(o->state)) {
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ }
}
/* Called from main context */
@@ -869,3 +868,30 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
return -PA_ERR_NOTIMPLEMENTED;
}
+
+void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) {
+ pa_proplist *pl = NULL;
+ pa_source_output_send_event_hook_data hook_data;
+
+ pa_source_output_assert_ref(o);
+ pa_assert(event);
+
+ if (!o->send_event)
+ return;
+
+ if (!data)
+ data = pl = pa_proplist_new();
+
+ hook_data.source_output = o;
+ hook_data.data = data;
+ hook_data.event = event;
+
+ if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0)
+ goto finish;
+
+ o->send_event(o, event, data);
+
+finish:
+ if (pl)
+ pa_proplist_free(pl);
+}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 91f28f94..9369568c 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -143,6 +143,10 @@ struct pa_source_output {
* will not be allowed */
pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */
+ /* If non-NULL this function is used to dispatch asynchronous
+ * control events. */
+ void (*send_event)(pa_source_output *o, const char *event, pa_proplist* data);
+
struct {
pa_source_output_state_t state;
@@ -177,6 +181,12 @@ enum {
PA_SOURCE_OUTPUT_MESSAGE_MAX
};
+typedef struct pa_source_output_send_event_hook_data {
+ pa_source_output *source_output;
+ const char *event;
+ pa_proplist *data;
+} pa_source_output_send_event_hook_data;
+
typedef struct pa_source_output_new_data {
pa_proplist *proplist;
pa_sink_input *direct_on_input;
@@ -229,10 +239,12 @@ void pa_source_output_kill(pa_source_output*o);
pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency);
-pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
+void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
+void pa_source_output_send_event(pa_source_output *o, const char *name, pa_proplist *data);
+
pa_bool_t pa_source_output_may_move(pa_source_output *o);
pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save);