summaryrefslogtreecommitdiffstats
path: root/src/pulse
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulse')
-rw-r--r--src/pulse/channelmap.c49
-rw-r--r--src/pulse/channelmap.h70
-rw-r--r--src/pulse/client-conf.c20
-rw-r--r--src/pulse/context.c62
-rw-r--r--src/pulse/context.h11
-rw-r--r--src/pulse/def.h75
-rw-r--r--src/pulse/ext-stream-restore.c48
-rw-r--r--src/pulse/ext-stream-restore.h8
-rw-r--r--src/pulse/internal.h7
-rw-r--r--src/pulse/operation.c1
-rw-r--r--src/pulse/proplist.c384
-rw-r--r--src/pulse/proplist.h10
-rw-r--r--src/pulse/sample.c38
-rw-r--r--src/pulse/sample.h4
-rw-r--r--src/pulse/stream.c59
-rw-r--r--src/pulse/stream.h14
-rw-r--r--src/pulse/utf8.c26
-rw-r--r--src/pulse/utf8.h6
-rw-r--r--src/pulse/volume.c62
-rw-r--r--src/pulse/volume.h8
20 files changed, 823 insertions, 139 deletions
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index 6ff30c26..983b8977 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -434,11 +434,11 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) {
const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) {
- pa_init_i18n();
-
if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
return NULL;
+ pa_init_i18n();
+
return _(pretty_table[pos]);
}
@@ -448,6 +448,9 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+ pa_return_val_if_fail(pa_channel_map_valid(b), 0);
+
if (a->channels != b->channels)
return 0;
@@ -502,19 +505,19 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
* "mono" here explicitly, because that can be understood as
* listing with one channel called "mono". */
- if (strcmp(s, "stereo") == 0) {
+ if (pa_streq(s, "stereo")) {
map.channels = 2;
map.map[0] = PA_CHANNEL_POSITION_LEFT;
map.map[1] = PA_CHANNEL_POSITION_RIGHT;
goto finish;
- } else if (strcmp(s, "surround-40") == 0) {
+ } else if (pa_streq(s, "surround-40")) {
map.channels = 4;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
goto finish;
- } else if (strcmp(s, "surround-41") == 0) {
+ } else if (pa_streq(s, "surround-41")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
@@ -522,7 +525,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_LFE;
goto finish;
- } else if (strcmp(s, "surround-50") == 0) {
+ } else if (pa_streq(s, "surround-50")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
@@ -530,7 +533,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
goto finish;
- } else if (strcmp(s, "surround-51") == 0) {
+ } else if (pa_streq(s, "surround-51")) {
map.channels = 6;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
@@ -539,7 +542,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
map.map[5] = PA_CHANNEL_POSITION_LFE;
goto finish;
- } else if (strcmp(s, "surround-71") == 0) {
+ } else if (pa_streq(s, "surround-71")) {
map.channels = 8;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
@@ -563,13 +566,13 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
}
/* Some special aliases */
- if (strcmp(p, "left") == 0)
+ if (pa_streq(p, "left"))
map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT;
- else if (strcmp(p, "right") == 0)
+ else if (pa_streq(p, "right"))
map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT;
- else if (strcmp(p, "center") == 0)
+ else if (pa_streq(p, "center"))
map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER;
- else if (strcmp(p, "subwoofer") == 0)
+ else if (pa_streq(p, "subwoofer"))
map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER;
else {
pa_channel_position_t i;
@@ -617,11 +620,8 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s
pa_assert(map);
pa_assert(ss);
- if (!pa_channel_map_valid(map))
- return 0;
-
- if (!pa_sample_spec_valid(ss))
- return 0;
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+ pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
return map->channels == ss->channels;
}
@@ -633,6 +633,9 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+ pa_return_val_if_fail(pa_channel_map_valid(b), 0);
+
memset(in_a, 0, sizeof(in_a));
for (i = 0; i < a->channels; i++)
@@ -651,6 +654,8 @@ int pa_channel_map_can_balance(const pa_channel_map *map) {
pa_assert(map);
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
for (c = 0; c < map->channels; c++) {
switch (map->map[c]) {
@@ -687,6 +692,10 @@ int pa_channel_map_can_fade(const pa_channel_map *map) {
unsigned c;
pa_bool_t front = FALSE, rear = FALSE;
+ pa_assert(map);
+
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0);
+
for (c = 0; c < map->channels; c++) {
switch (map->map[c]) {
@@ -727,6 +736,8 @@ const char* pa_channel_map_to_name(const pa_channel_map *map) {
pa_assert(map);
+ pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+
memset(in_map, 0, sizeof(in_map));
for (c = 0; c < map->channels; c++)
@@ -779,11 +790,15 @@ const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
pa_assert(map);
+ pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+
memset(in_map, 0, sizeof(in_map));
for (c = 0; c < map->channels; c++)
pa_bitset_set(in_map, map->map[c], TRUE);
+ pa_init_i18n();
+
if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
PA_CHANNEL_POSITION_MONO, -1))
return _("Mono");
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index f9124b2e..a6d37d85 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -141,6 +141,66 @@ typedef enum pa_channel_position {
PA_CHANNEL_POSITION_MAX
} pa_channel_position_t;
+/** \cond fulldocs */
+#define PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
+#define PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
+#define PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
+#define PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
+#define PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
+#define PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
+#define PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
+#define PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
+#define PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
+#define PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
+#define PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
+#define PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
+#define PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
+#define PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
+#define PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
+#define PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
+#define PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
+#define PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
+#define PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
+#define PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
+#define PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
+#define PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
+#define PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
+#define PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
+#define PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
+#define PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
+#define PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
+#define PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
+#define PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
+#define PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
+#define PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
+#define PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
+#define PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
+#define PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
+#define PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
+#define PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
+#define PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
+#define PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
+#define PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
+#define PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
+#define PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
+#define PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
+#define PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
+#define PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
+#define PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
+#define PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
+#define PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
+#define PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
+#define PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
+#define PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
+#define PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
+#define PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
+#define PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
+#define PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
+#define PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
+#define PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
+#define PA_CHANNEL_POSITION_MAX PA_CHANNEL_POSITION_MAX
+/** \endcond */
+
/** A list of channel mapping definitions for pa_channel_map_init_auto() */
typedef enum pa_channel_map_def {
PA_CHANNEL_MAP_AIFF,
@@ -165,6 +225,16 @@ typedef enum pa_channel_map_def {
/**< The default channel map */
} pa_channel_map_def_t;
+/** \cond fulldocs */
+#define PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
+#define PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
+#define PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
+#define PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
+#define PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
+#define PA_CHANNEL_MAP_DEF_MAX PA_CHANNEL_MAP_DEF_MAX
+#define PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
+/** \endcond */
+
/** A channel map which can be used to attach labels to specific
* channels of a stream. These values are relevant for conversion and
* mixing of streams */
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index 58d64642..71f8443d 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -92,16 +92,16 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
/* Prepare the configuration parse table */
pa_config_item table[] = {
- { "daemon-binary", pa_config_parse_string, NULL },
- { "extra-arguments", pa_config_parse_string, NULL },
- { "default-sink", pa_config_parse_string, NULL },
- { "default-source", pa_config_parse_string, NULL },
- { "default-server", pa_config_parse_string, NULL },
- { "autospawn", pa_config_parse_bool, NULL },
- { "cookie-file", pa_config_parse_string, NULL },
- { "disable-shm", pa_config_parse_bool, NULL },
- { "shm-size-bytes", pa_config_parse_size, NULL },
- { NULL, NULL, NULL },
+ { "daemon-binary", pa_config_parse_string, NULL, NULL },
+ { "extra-arguments", pa_config_parse_string, NULL, NULL },
+ { "default-sink", pa_config_parse_string, NULL, NULL },
+ { "default-source", pa_config_parse_string, NULL, NULL },
+ { "default-server", pa_config_parse_string, NULL, NULL },
+ { "autospawn", pa_config_parse_bool, NULL, NULL },
+ { "cookie-file", pa_config_parse_string, NULL, NULL },
+ { "disable-shm", pa_config_parse_bool, NULL, NULL },
+ { "shm-size-bytes", pa_config_parse_size, NULL, NULL },
+ { NULL, NULL, NULL, NULL },
};
table[0].data = &c->daemon_binary;
diff --git a/src/pulse/context.c b/src/pulse/context.c
index d41e62e2..81050914 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -95,7 +95,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
[PA_COMMAND_STARTED] = pa_command_stream_started,
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event,
- [PA_COMMAND_EXTENSION] = pa_command_extension
+ [PA_COMMAND_EXTENSION] = pa_command_extension,
+ [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event,
+ [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event,
+ [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event
};
static void context_free(pa_context *c);
@@ -112,6 +115,9 @@ static void reset_callbacks(pa_context *c) {
c->subscribe_callback = NULL;
c->subscribe_userdata = NULL;
+ c->event_callback = NULL;
+ c->event_userdata = NULL;
+
c->ext_stream_restore.callback = NULL;
c->ext_stream_restore.userdata = NULL;
}
@@ -917,6 +923,17 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi
c->state_userdata = userdata;
}
+void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+ return;
+
+ c->event_callback = cb;
+ c->event_userdata = userdata;
+}
+
int pa_context_is_pending(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -1245,6 +1262,11 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
pa_context_ref(c);
+ if (c->version < 15) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_gets(t, &name) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
@@ -1259,3 +1281,41 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
finish:
pa_context_unref(c);
}
+
+
+void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_proplist *pl = NULL;
+ const char *event;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_CLIENT_EVENT);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 15) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ pl = pa_proplist_new();
+
+ if (pa_tagstruct_gets(t, &event) < 0 ||
+ pa_tagstruct_get_proplist(t, pl) < 0 ||
+ !pa_tagstruct_eof(t) || !event) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (c->event_callback)
+ c->event_callback(c, event, pl, c->event_userdata);
+
+finish:
+ pa_context_unref(c);
+
+ if (pl)
+ pa_proplist_free(pl);
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
index dfb7e4a1..2ae4c013 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -164,6 +164,13 @@ typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
/** A generic callback for operation completion */
typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
+/** A callback for asynchronous meta/policy event messages. The set
+ * of defined events can be extended at any time. Also, server modules
+ * may introduce additional message types so make sure that your
+ * callback function ignores messages it doesn't know. \since
+ * 0.9.15 */
+typedef void (*pa_context_event_cb_t)(pa_context *c, const char *name, pa_proplist *p, void *userdata);
+
/** Instantiate a new connection context with an abstract mainloop API
* and an application name. It is recommended to use pa_context_new_with_proplist()
* instead and specify some initial properties.*/
@@ -183,6 +190,10 @@ pa_context* pa_context_ref(pa_context *c);
/** Set a callback function that is called whenever the context status changes */
void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+/** Set a callback function that is called whenver a meta/policy
+ * control event is received. \since 0.9.15 */
+void pa_context_set_event_callback(pa_context *p, pa_context_event_cb_t cb, void *userdata);
+
/** Return the error number of the last failed operation */
int pa_context_errno(pa_context *c);
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 61498883..7f3a0c5d 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -57,6 +57,12 @@ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
}
/** \cond fulldocs */
+#define PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
+#define PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
+#define PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
+#define PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
+#define PA_CONTEXT_READY PA_CONTEXT_READY
+#define PA_CONTEXT_FAILED PA_CONTEXT_FAILED
#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD
/** \endcond */
@@ -77,6 +83,11 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
}
/** \cond fulldocs */
+#define PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
+#define PA_STREAM_CREATING PA_STREAM_CREATING
+#define PA_STREAM_READY PA_STREAM_READY
+#define PA_STREAM_FAILED PA_STREAM_FAILED
+#define PA_STREAM_TERMINATED PA_STREAM_TERMINATED
#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD
/** \endcond */
@@ -87,6 +98,12 @@ typedef enum pa_operation_state {
PA_OPERATION_CANCELED /**< The operation has been canceled */
} pa_operation_state_t;
+/** \cond fulldocs */
+#define PA_OPERATION_RUNNING PA_OPERATION_RUNNING
+#define PA_OPERATION_DONE PA_OPERATION_DONE
+#define PA_OPERATION_CANCELED PA_OPERATION_CANCELED
+/** \endcond */
+
/** An invalid index */
#define PA_INVALID_INDEX ((uint32_t) -1)
@@ -109,6 +126,13 @@ typedef enum pa_stream_direction {
PA_STREAM_UPLOAD /**< Sample upload stream */
} pa_stream_direction_t;
+/** \cond fulldocs */
+#define PA_STREAM_NODIRECTION PA_STREAM_NODIRECTION
+#define PA_STREAM_PLAYBACK PA_STREAM_PLAYBACK
+#define PA_STREAM_RECORD PA_STREAM_RECORD
+#define PA_STREAM_UPLOAD PA_STREAM_UPLOAD
+/** \endcond */
+
/** Some special flags for stream connections. */
typedef enum pa_stream_flags {
@@ -491,6 +515,36 @@ typedef enum pa_subscription_event_type {
/** Return one if an event type t matches an event mask bitfield */
#define pa_subscription_match_flags(m, t) (!!((m) & (1 << ((t) & PA_SUBSCRIPTION_EVENT_FACILITY_MASK))))
+/** \cond fulldocs */
+#define PA_SUBSCRIPTION_MASK_NULL PA_SUBSCRIPTION_MASK_NULL
+#define PA_SUBSCRIPTION_MASK_SINK PA_SUBSCRIPTION_MASK_SINK
+#define PA_SUBSCRIPTION_MASK_SOURCE PA_SUBSCRIPTION_MASK_SOURCE
+#define PA_SUBSCRIPTION_MASK_SINK_INPUT PA_SUBSCRIPTION_MASK_SINK_INPUT
+#define PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT
+#define PA_SUBSCRIPTION_MASK_MODULE PA_SUBSCRIPTION_MASK_MODULE
+#define PA_SUBSCRIPTION_MASK_CLIENT PA_SUBSCRIPTION_MASK_CLIENT
+#define PA_SUBSCRIPTION_MASK_SAMPLE_CACHE PA_SUBSCRIPTION_MASK_SAMPLE_CACHE
+#define PA_SUBSCRIPTION_MASK_SERVER PA_SUBSCRIPTION_MASK_SERVER
+#define PA_SUBSCRIPTION_MASK_AUTOLOAD PA_SUBSCRIPTION_MASK_AUTOLOAD
+#define PA_SUBSCRIPTION_MASK_CARD PA_SUBSCRIPTION_MASK_CARD
+#define PA_SUBSCRIPTION_MASK_ALL PA_SUBSCRIPTION_MASK_ALL
+#define PA_SUBSCRIPTION_EVENT_SINK PA_SUBSCRIPTION_EVENT_SINK
+#define PA_SUBSCRIPTION_EVENT_SOURCE PA_SUBSCRIPTION_EVENT_SOURCE
+#define PA_SUBSCRIPTION_EVENT_SINK_INPUT PA_SUBSCRIPTION_EVENT_SINK_INPUT
+#define PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT
+#define PA_SUBSCRIPTION_EVENT_MODULE PA_SUBSCRIPTION_EVENT_MODULE
+#define PA_SUBSCRIPTION_EVENT_CLIENT PA_SUBSCRIPTION_EVENT_CLIENT
+#define PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE
+#define PA_SUBSCRIPTION_EVENT_SERVER PA_SUBSCRIPTION_EVENT_SERVER
+#define PA_SUBSCRIPTION_EVENT_AUTOLOAD PA_SUBSCRIPTION_EVENT_AUTOLOAD
+#define PA_SUBSCRIPTION_EVENT_CARD PA_SUBSCRIPTION_EVENT_CARD
+#define PA_SUBSCRIPTION_EVENT_FACILITY_MASK PA_SUBSCRIPTION_EVENT_FACILITY_MASK
+#define PA_SUBSCRIPTION_EVENT_NEW PA_SUBSCRIPTION_EVENT_NEW
+#define PA_SUBSCRIPTION_EVENT_CHANGE PA_SUBSCRIPTION_EVENT_CHANGE
+#define PA_SUBSCRIPTION_EVENT_REMOVE PA_SUBSCRIPTION_EVENT_REMOVE
+#define PA_SUBSCRIPTION_EVENT_TYPE_MASK PA_SUBSCRIPTION_EVENT_TYPE_MASK
+/** \endcond */
+
/** A structure for all kinds of timing information of a stream. See
* pa_stream_update_timing_info() and pa_stream_get_timing_info(). The
* total output latency a sample that is written with
@@ -617,6 +671,13 @@ typedef enum pa_seek_mode {
/**< Seek relatively to the current end of the buffer queue. */
} pa_seek_mode_t;
+/** \cond fulldocs */
+#define PA_SEEK_RELATIVE PA_SEEK_RELATIVE
+#define PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
+#define PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
+#define PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
+/** \endcond */
+
/** Special sink flags. */
typedef enum pa_sink_flags {
PA_SINK_HW_VOLUME_CTRL = 0x0001U,
@@ -694,6 +755,8 @@ static inline int PA_SINK_IS_OPENED(pa_sink_state_t x) {
#define PA_SINK_RUNNING PA_SINK_RUNNING
#define PA_SINK_IDLE PA_SINK_IDLE
#define PA_SINK_SUSPENDED PA_SINK_SUSPENDED
+#define PA_SINK_INIT PA_SINK_INIT
+#define PA_SINK_UNLINKED PA_SINK_UNLINKED
#define PA_SINK_IS_OPENED PA_SINK_IS_OPENED
/** \endcond */
@@ -769,12 +832,24 @@ static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) {
#define PA_SOURCE_RUNNING PA_SOURCE_RUNNING
#define PA_SOURCE_IDLE PA_SOURCE_IDLE
#define PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
+#define PA_SOURCE_INIT PA_SOURCE_INIT
+#define PA_SOURCE_UNLINKED PA_SOURCE_UNLINKED
#define PA_SOURCE_IS_OPENED PA_SOURCE_IS_OPENED
/** \endcond */
/** A generic free() like callback prototype */
typedef void (*pa_free_cb_t)(void *p);
+/** A stream policy/meta event requesting that an application should
+ * cork a specific stream. See pa_stream_event_cb_t for more
+ * information, \since 0.9.15 */
+#define PA_STREAM_EVENT_REQUEST_CORK "request-cork"
+
+/** A stream policy/meta event requesting that an application should
+ * cork a specific stream. See pa_stream_event_cb_t for more
+ * information, \since 0.9.15 */
+#define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork"
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c
index 703179c5..469c822a 100644
--- a/src/pulse/ext-stream-restore.c
+++ b/src/pulse/ext-stream-restore.c
@@ -30,6 +30,7 @@
#include <pulsecore/pstream-util.h>
#include "internal.h"
+#include "operation.h"
#include "ext-stream-restore.h"
@@ -191,8 +192,8 @@ pa_operation *pa_ext_stream_restore_write(
void *userdata) {
uint32_t tag;
- pa_operation *o;
- pa_tagstruct *t;
+ pa_operation *o = NULL;
+ pa_tagstruct *t = NULL;
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -213,7 +214,15 @@ pa_operation *pa_ext_stream_restore_write(
pa_tagstruct_put_boolean(t, apply_immediately);
for (; n > 0; n--, data++) {
+ if (!data->name || !*data->name)
+ goto fail;
+
pa_tagstruct_puts(t, data->name);
+
+ if (data->volume.channels > 0 &&
+ !pa_cvolume_compatible_with_channel_map(&data->volume, &data->channel_map))
+ goto fail;
+
pa_tagstruct_put_channel_map(t, &data->channel_map);
pa_tagstruct_put_cvolume(t, &data->volume);
pa_tagstruct_puts(t, data->device);
@@ -224,6 +233,18 @@ pa_operation *pa_ext_stream_restore_write(
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
+
+fail:
+ if (o) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ }
+
+ if (t)
+ pa_tagstruct_free(t);
+
+ pa_context_set_error(c, PA_ERR_INVALID);
+ return NULL;
}
pa_operation *pa_ext_stream_restore_delete(
@@ -233,8 +254,8 @@ pa_operation *pa_ext_stream_restore_delete(
void *userdata) {
uint32_t tag;
- pa_operation *o;
- pa_tagstruct *t;
+ pa_operation *o = NULL;
+ pa_tagstruct *t = NULL;
const char *const *k;
pa_assert(c);
@@ -251,13 +272,29 @@ pa_operation *pa_ext_stream_restore_delete(
pa_tagstruct_puts(t, "module-stream-restore");
pa_tagstruct_putu32(t, SUBCOMMAND_DELETE);
- for (k = s; *k; k++)
+ for (k = s; *k; k++) {
+ if (!*k || !**k)
+ goto fail;
+
pa_tagstruct_puts(t, *k);
+ }
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
+
+fail:
+ if (o) {
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
+ }
+
+ if (t)
+ pa_tagstruct_free(t);
+
+ pa_context_set_error(c, PA_ERR_INVALID);
+ return NULL;
}
pa_operation *pa_ext_stream_restore_subscribe(
@@ -322,5 +359,4 @@ void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t)
if (c->ext_stream_restore.callback)
c->ext_stream_restore.callback(c, c->ext_stream_restore.userdata);
-
}
diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h
index ac01d235..cf9f4ccc 100644
--- a/src/pulse/ext-stream-restore.h
+++ b/src/pulse/ext-stream-restore.h
@@ -36,10 +36,10 @@ PA_C_DECL_BEGIN
* maintained by module-stream-restore. \since 0.9.12 */
typedef struct pa_ext_stream_restore_info {
const char *name; /**< Identifier string of the stream. A string like "sink-input-by-role:" or similar followed by some arbitrary property value. */
- pa_channel_map channel_map; /**< The channel map for the volume field */
- pa_cvolume volume; /**< The volume of the stream when it was seen last, if applicable */
- const char *device; /**< The sink/source of the stream when it was last seen */
- int mute; /**< The boolean mute state of the stream when it was last seen, if applicable */
+ pa_channel_map channel_map; /**< The channel map for the volume field, if applicable */
+ pa_cvolume volume; /**< The volume of the stream when it was seen last, if applicable and saved */
+ const char *device; /**< The sink/source of the stream when it was last seen, if applicable and saved */
+ int mute; /**< The boolean mute state of the stream when it was last seen, if applicable and saved */
} pa_ext_stream_restore_info;
/** Callback prototype for pa_ext_stream_restore_test(). \since 0.9.12 */
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 9a2d6457..e533625d 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -71,6 +71,8 @@ struct pa_context {
void *state_userdata;
pa_context_subscribe_cb_t subscribe_callback;
void *subscribe_userdata;
+ pa_context_event_cb_t event_callback;
+ void *event_userdata;
pa_mempool *mempool;
@@ -181,6 +183,8 @@ struct pa_stream {
void *suspended_userdata;
pa_stream_notify_cb_t started_callback;
void *started_userdata;
+ pa_stream_event_cb_t event_callback;
+ void *event_userdata;
};
typedef void (*pa_operation_cb_t)(void);
@@ -207,6 +211,9 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32
void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
void pa_operation_done(pa_operation *o);
diff --git a/src/pulse/operation.c b/src/pulse/operation.c
index 13b470a8..aa2bbc05 100644
--- a/src/pulse/operation.c
+++ b/src/pulse/operation.c
@@ -62,6 +62,7 @@ pa_operation *pa_operation_ref(pa_operation *o) {
PA_REFCNT_INC(o);
return o;
}
+
void pa_operation_unref(pa_operation *o) {
pa_assert(o);
pa_assert(PA_REFCNT_VALUE(o) >= 1);
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index 282fe5cc..db4c9344 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -24,6 +24,7 @@
#endif
#include <string.h>
+#include <ctype.h>
#include <pulse/xmalloc.h>
#include <pulse/utf8.h>
@@ -46,7 +47,7 @@ struct property {
static pa_bool_t property_name_valid(const char *key) {
- if (!pa_utf8_valid(key))
+ if (!pa_ascii_valid(key))
return FALSE;
if (strlen(key) <= 0)
@@ -64,8 +65,6 @@ static void property_free(struct property *prop) {
}
pa_proplist* pa_proplist_new(void) {
- pa_init_i18n();
-
return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func));
}
@@ -83,6 +82,7 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
pa_assert(p);
pa_assert(key);
+ pa_assert(value);
if (!property_name_valid(key) || !pa_utf8_valid(value))
return -1;
@@ -104,25 +104,130 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
}
/** Will accept only valid UTF-8 */
+static int proplist_setn(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
+ char *k, *v;
+
+ pa_assert(p);
+ pa_assert(key);
+ pa_assert(value);
+
+ k = pa_xstrndup(key, key_length);
+ v = pa_xstrndup(value, value_length);
+
+ if (!property_name_valid(k) || !pa_utf8_valid(v)) {
+ pa_xfree(k);
+ pa_xfree(v);
+ return -1;
+ }
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = k;
+ add = TRUE;
+ } else {
+ pa_xfree(prop->value);
+ pa_xfree(k);
+ }
+
+ prop->value = v;
+ prop->nbytes = strlen(v)+1;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+}
+
+static int proplist_sethex(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
+ char *k, *v;
+ uint8_t *d;
+ size_t dn;
+
+ pa_assert(p);
+ pa_assert(key);
+ pa_assert(value);
+
+ k = pa_xstrndup(key, key_length);
+
+ if (!property_name_valid(k)) {
+ pa_xfree(k);
+ return -1;
+ }
+
+ v = pa_xstrndup(value, value_length);
+ d = pa_xmalloc(value_length*2+1);
+
+ if ((dn = pa_parsehex(v, d, value_length*2)) == (size_t) -1) {
+ pa_xfree(k);
+ pa_xfree(v);
+ pa_xfree(d);
+ return -1;
+ }
+
+ pa_xfree(v);
+
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = k;
+ add = TRUE;
+ } else {
+ pa_xfree(prop->value);
+ pa_xfree(k);
+ }
+
+ d[dn] = 0;
+ prop->value = d;
+ prop->nbytes = dn;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+}
+
+/** Will accept only valid UTF-8 */
int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) {
+ struct property *prop;
+ pa_bool_t add = FALSE;
va_list ap;
- int r;
- char *t;
+ char *v;
pa_assert(p);
pa_assert(key);
+ pa_assert(format);
if (!property_name_valid(key) || !pa_utf8_valid(format))
return -1;
va_start(ap, format);
- t = pa_vsprintf_malloc(format, ap);
+ v = pa_vsprintf_malloc(format, ap);
va_end(ap);
- r = pa_proplist_sets(p, key, t);
+ if (!pa_utf8_valid(v))
+ goto fail;
- pa_xfree(t);
- return r;
+ if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+ prop = pa_xnew(struct property, 1);
+ prop->key = pa_xstrdup(key);
+ add = TRUE;
+ } else
+ pa_xfree(prop->value);
+
+ prop->value = v;
+ prop->nbytes = strlen(v)+1;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
+
+ return 0;
+
+fail:
+ pa_xfree(v);
+ return -1;
}
int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
@@ -131,6 +236,7 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb
pa_assert(p);
pa_assert(key);
+ pa_assert(data);
if (!property_name_valid(key))
return -1;
@@ -142,7 +248,9 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb
} else
pa_xfree(prop->value);
- prop->value = pa_xmemdup(data, nbytes);
+ prop->value = pa_xmalloc(nbytes+1);
+ memcpy(prop->value, data, nbytes);
+ ((char*) prop->value)[nbytes] = 0;
prop->nbytes = nbytes;
if (add)
@@ -183,6 +291,8 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *
pa_assert(p);
pa_assert(key);
+ pa_assert(data);
+ pa_assert(nbytes);
if (!property_name_valid(key))
return -1;
@@ -275,9 +385,32 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) {
if (!pa_strbuf_isempty(buf))
pa_strbuf_puts(buf, sep);
- if ((v = pa_proplist_gets(p, key)))
- pa_strbuf_printf(buf, "%s = \"%s\"", key, v);
- else {
+ if ((v = pa_proplist_gets(p, key))) {
+ const char *t;
+
+ pa_strbuf_printf(buf, "%s = \"", key);
+
+ for (t = v;;) {
+ size_t h;
+
+ h = strcspn(t, "\"");
+
+ if (h > 0)
+ pa_strbuf_putsn(buf, t, h);
+
+ t += h;
+
+ if (*t == 0)
+ break;
+
+ pa_assert(*t == '"');
+ pa_strbuf_puts(buf, "\\\"");
+
+ t++;
+ }
+
+ pa_strbuf_puts(buf, "\"");
+ } else {
const void *value;
size_t nbytes;
char *c;
@@ -304,88 +437,189 @@ char *pa_proplist_to_string(pa_proplist *p) {
return t;
}
-/* Remove all whitepsapce from the beginning and the end of *s. *s may
- * be modified. (from conf-parser.c) */
-#define WHITESPACE " \t\n"
-#define in_string(c,s) (strchr(s,c) != NULL)
-
-static char *strip(char *s) {
- char *b = s+strspn(s, WHITESPACE);
- char *e, *l = NULL;
-
- for (e = b; *e; e++)
- if (!in_string(*e, WHITESPACE))
- l = e;
+pa_proplist *pa_proplist_from_string(const char *s) {
+ enum {
+ WHITESPACE,
+ KEY,
+ AFTER_KEY,
+ VALUE_START,
+ VALUE_SIMPLE,
+ VALUE_DOUBLE_QUOTES,
+ VALUE_DOUBLE_QUOTES_ESCAPE,
+ VALUE_TICKS,
+ VALUE_TICKS_ESCAPED,
+ VALUE_HEX
+ } state;
+
+ pa_proplist *pl;
+ const char *p, *key = NULL, *value = NULL;
+ size_t key_len = 0, value_len = 0;
+
+ pa_assert(s);
+
+ pl = pa_proplist_new();
+
+ state = WHITESPACE;
+
+ for (p = s;; p++) {
+ switch (state) {
+
+ case WHITESPACE:
+ if (*p == 0)
+ goto success;
+ else if (*p == '=')
+ goto fail;
+ else if (!isspace(*p)) {
+ key = p;
+ state = KEY;
+ key_len = 1;
+ }
+ break;
- if (l)
- *(l+1) = 0;
+ case KEY:
+ if (*p == 0)
+ goto fail;
+ else if (*p == '=')
+ state = VALUE_START;
+ else if (isspace(*p))
+ state = AFTER_KEY;
+ else
+ key_len++;
+ break;
- return b;
-}
+ case AFTER_KEY:
+ if (*p == 0)
+ goto fail;
+ else if (*p == '=')
+ state = VALUE_START;
+ else if (!isspace(*p))
+ goto fail;
+ break;
-pa_proplist *pa_proplist_from_string(const char *str) {
- pa_proplist *p;
- char *s, *v, *k, *e;
+ case VALUE_START:
+ if (*p == 0)
+ goto fail;
+ else if (strncmp(p, "hex:", 4) == 0) {
+ state = VALUE_HEX;
+ value = p+4;
+ value_len = 0;
+ p += 3;
+ } else 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)) {
+ state = VALUE_SIMPLE;
+ value = p;
+ value_len = 1;
+ }
+ break;
- pa_assert(str);
- pa_assert_se(p = pa_proplist_new());
- pa_assert_se(s = strdup(str));
+ case VALUE_SIMPLE:
+ if (*p == 0 || isspace(*p)) {
+ if (proplist_setn(pl, key, key_len, value, value_len) < 0)
+ goto fail;
- for (k = s; *k; k = e) {
- k = k+strspn(k, WHITESPACE);
+ if (*p == 0)
+ goto success;
- if (!*k)
- break;
+ state = WHITESPACE;
+ } else
+ value_len++;
+ break;
- if (!(v = strchr(k, '='))) {
- pa_log("Missing '='.");
- break;
- }
+ case VALUE_DOUBLE_QUOTES:
+ if (*p == 0)
+ goto fail;
+ else if (*p == '"') {
+ char *v;
+
+ v = pa_unescape(pa_xstrndup(value, value_len));
+
+ if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) {
+ pa_xfree(v);
+ goto fail;
+ }
+
+ pa_xfree(v);
+ state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_DOUBLE_QUOTES_ESCAPE;
+ value_len++;
+ } else
+ value_len++;
+ break;
- *v++ = '\0';
- k = strip(k);
+ case VALUE_DOUBLE_QUOTES_ESCAPE:
+ if (*p == 0)
+ goto fail;
+ else {
+ state = VALUE_DOUBLE_QUOTES;
+ value_len++;
+ }
+ break;
- v = v+strspn(v, WHITESPACE);
- if (*v == '"') {
- v++;
- if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */
- pa_log("Missing '\"' at end of string value.");
+ case VALUE_TICKS:
+ if (*p == 0)
+ goto fail;
+ else if (*p == '\'') {
+ char *v;
+
+ v = pa_unescape(pa_xstrndup(value, value_len));
+
+ if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) {
+ pa_xfree(v);
+ goto fail;
+ }
+
+ pa_xfree(v);
+ state = WHITESPACE;
+ } else if (*p == '\\') {
+ state = VALUE_TICKS_ESCAPED;
+ value_len++;
+ } else
+ value_len++;
break;
- }
- *e++ = '\0';
- pa_proplist_sets(p, k, v);
- } else {
- uint8_t *blob;
- if (*v++ != 'h' || *v++ != 'e' || *v++ != 'x' || *v++ != ':') {
- pa_log("Value must be a string or \"hex:\"");
+ case VALUE_TICKS_ESCAPED:
+ if (*p == 0)
+ goto fail;
+ else {
+ state = VALUE_TICKS;
+ value_len++;
+ }
break;
- }
- e = v;
- while (in_string(*e, "0123456789abcdefABCDEF"))
- ++e;
+ case VALUE_HEX:
+ if ((*p >= '0' && *p <= '9') ||
+ (*p >= 'A' && *p <= 'F') ||
+ (*p >= 'a' && *p <= 'f')) {
+ value_len++;
+ } else if (*p == 0 || isspace(*p)) {
- if ((e - v) % 2) {
- pa_log("Invalid \"hex:\" value data");
- break;
- }
+ if (proplist_sethex(pl, key, key_len, value, value_len) < 0)
+ goto fail;
- blob = pa_xmalloc((size_t)(e-v)/2);
- if (pa_parsehex(v, blob, (e-v)/2) != (size_t)((e-v)/2)) {
- pa_log("Invalid \"hex:\" value data");
- pa_xfree(blob);
- break;
- }
+ if (*p == 0)
+ goto success;
- pa_proplist_set(p, k, blob, (e-v)/2);
- pa_xfree(blob);
+ state = WHITESPACE;
+ } else
+ goto fail;
+ break;
}
}
- pa_xfree(s);
+success:
+ return MAKE_PROPLIST(pl);
- return p;
+fail:
+ pa_proplist_free(pl);
+ return NULL;
}
int pa_proplist_contains(pa_proplist *p, const char *key) {
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 990ffd19..57a23d9f 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -39,7 +39,7 @@ PA_C_DECL_BEGIN
* media.filename
* media.icon Binary blob containing PNG icon data
* media.icon_name Name from XDG icon naming spec
- * media.role video, music, game, event, phone, production, filter, abstract, stream
+ * media.role video, music, game, event, phone, animation, production, filter, abstract, stream
* event.id Name from XDG sound naming spec
* event.description "Button blabla clicked" for a11y
* event.mouse.x
@@ -136,7 +136,7 @@ PA_C_DECL_BEGIN
#define PA_PROP_MODULE_USAGE "module.usage"
#define PA_PROP_MODULE_VERSION "module.version"
-/** A property list object. Basically a dictionary with UTF-8 strings
+/** A property list object. Basically a dictionary with ASCII strings
* as keys and arbitrary data as values. \since 0.9.11 */
typedef struct pa_proplist pa_proplist;
@@ -194,6 +194,12 @@ typedef enum pa_update_mode {
* list. */
} pa_update_mode_t;
+/** \cond fulldocs */
+#define PA_UPDATE_SET PA_UPDATE_SET
+#define PA_UPDATE_MERGE PA_UPDATE_MERGE
+#define PA_UPDATE_REPLACE PA_UPDATE_REPLACE
+/** \endcond */
+
/** Merge property list "other" into "p", adhering the merge mode as
* specified in "mode". \since 0.9.11 */
void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other);
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index a6c77345..4b13a337 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -36,7 +36,7 @@
#include "sample.h"
-size_t pa_sample_size(const pa_sample_spec *spec) {
+size_t pa_sample_size_of_format(pa_sample_format_t f) {
static const size_t table[] = {
[PA_SAMPLE_U8] = 1,
@@ -54,32 +54,44 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
[PA_SAMPLE_S24_32BE] = 4
};
+ pa_assert(f >= 0);
+ pa_assert(f < PA_SAMPLE_MAX);
+
+ return table[f];
+}
+
+size_t pa_sample_size(const pa_sample_spec *spec) {
+
pa_assert(spec);
- pa_assert(spec->format >= 0);
- pa_assert(spec->format < PA_SAMPLE_MAX);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
- return table[spec->format];
+ return pa_sample_size_of_format(spec->format);
}
size_t pa_frame_size(const pa_sample_spec *spec) {
pa_assert(spec);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return pa_sample_size(spec) * spec->channels;
}
size_t pa_bytes_per_second(const pa_sample_spec *spec) {
pa_assert(spec);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
+
return spec->rate*pa_frame_size(spec);
}
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
pa_assert(spec);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate);
}
size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
pa_assert(spec);
+ pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);
}
@@ -112,6 +124,9 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_sample_spec_valid(a), 0);
+ pa_return_val_if_fail(pa_sample_spec_valid(b), 0);
+
return
(a->format == b->format) &&
(a->rate == b->rate) &&
@@ -143,7 +158,7 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) {
char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
pa_assert(s);
- pa_assert(l);
+ pa_assert(l > 0);
pa_assert(spec);
pa_init_i18n();
@@ -151,22 +166,25 @@ char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) {
if (!pa_sample_spec_valid(spec))
pa_snprintf(s, l, _("(invalid)"));
else
- pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
+ pa_snprintf(s, l, _("%s %uch %uHz"), pa_sample_format_to_string(spec->format), spec->channels, spec->rate);
return s;
}
char* pa_bytes_snprint(char *s, size_t l, unsigned v) {
pa_assert(s);
+ pa_assert(l > 0);
+
+ pa_init_i18n();
if (v >= ((unsigned) 1024)*1024*1024)
- pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024);
+ pa_snprintf(s, l, _("%0.1f GiB"), ((double) v)/1024/1024/1024);
else if (v >= ((unsigned) 1024)*1024)
- pa_snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024);
+ pa_snprintf(s, l, _("%0.1f MiB"), ((double) v)/1024/1024);
else if (v >= (unsigned) 1024)
- pa_snprintf(s, l, "%0.1f KiB", ((double) v)/1024);
+ pa_snprintf(s, l, _("%0.1f KiB"), ((double) v)/1024);
else
- pa_snprintf(s, l, "%u B", (unsigned) v);
+ pa_snprintf(s, l, _("%u B"), (unsigned) v);
return s;
}
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 45a481fe..3c05b54a 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -261,6 +261,10 @@ size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE;
/** Return the size of a sample with the specific sample type */
size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE;
+/** Similar to pa_sample_size() but take a sample format instead of a
+ * full sample spec. \since 0.9.15 */
+size_t pa_sample_size_of_format(pa_sample_format_t f) PA_GCC_PURE;
+
/** Calculate the time the specified bytes take to play with the
* specified sample type. The return value will always be rounded
* down for non-integral return values. */
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index fe2514d9..b36bf9b1 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -69,6 +69,8 @@ static void reset_callbacks(pa_stream *s) {
s->suspended_userdata = NULL;
s->started_callback = NULL;
s->started_userdata = NULL;
+ s->event_callback = NULL;
+ s->event_userdata = NULL;
}
pa_stream *pa_stream_new_with_proplist(
@@ -565,6 +567,52 @@ finish:
pa_context_unref(c);
}
+void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+ pa_proplist *pl = NULL;
+ const char *event;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_EVENT || command == PA_COMMAND_RECORD_STREAM_EVENT);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 15) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ pl = pa_proplist_new();
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_gets(t, &event) < 0 ||
+ pa_tagstruct_get_proplist(t, pl) < 0 ||
+ !pa_tagstruct_eof(t) || !event) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_EVENT ? c->playback_streams : c->record_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (s->event_callback)
+ s->event_callback(s, event, pl, s->event_userdata);
+
+finish:
+ pa_context_unref(c);
+
+ if (pl)
+ pa_proplist_free(pl);
+}
+
void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s;
pa_context *c = userdata;
@@ -1696,6 +1744,17 @@ void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void
s->started_userdata = userdata;
}
+void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->event_callback = cb;
+ s->event_userdata = userdata;
+}
+
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 1bec1ebf..3965e9a2 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -324,6 +324,14 @@ typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdat
/** A generic notification callback */
typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
+/** A callback for asynchronous meta/policy event messages. Well known
+ * event names are PA_STREAM_EVENT_REQUEST_CORK and
+ * PA_STREAM_EVENT_REQUEST_UNCORK. The set of defined events can be
+ * extended at any time. Also, server modules may introduce additional
+ * message types so make sure that your callback function ignores messages
+ * it doesn't know. \since 0.9.15 */
+typedef void (*pa_stream_event_cb_t)(pa_stream *p, const char *name, pa_proplist *pl, void *userdata);
+
/** Create a new, unconnected stream with the specified name and
* sample type. It is recommended to use pa_stream_new_with_proplist()
* instead and specify some initial properties. */
@@ -500,6 +508,10 @@ void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *
* 0.9.8. \since 0.9.8 */
void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+/** Set the callback function that is called whenver a meta/policy
+ * control event is received.\since 0.9.15 */
+void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata);
+
/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
@@ -518,7 +530,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us
* temporarily. Available for playback streams only. */
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
-/** Rename the stream. */
+/** Rename the stream.*/
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
/** Return the current playback/recording time. This is based on the
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
index 7671be46..6b58bde3 100644
--- a/src/pulse/utf8.c
+++ b/src/pulse/utf8.c
@@ -263,3 +263,29 @@ char* pa_locale_to_utf8 (const char *str) {
}
#endif
+
+char *pa_ascii_valid(const char *str) {
+ const char *p;
+ pa_assert(str);
+
+ for (p = str; *p; p++)
+ if ((unsigned char) *p >= 128)
+ return NULL;
+
+ return (char*) str;
+}
+
+char *pa_ascii_filter(const char *str) {
+ char *r, *s, *d;
+ pa_assert(str);
+
+ r = pa_xstrdup(str);
+
+ for (s = r, d = r; *s; s++)
+ if ((unsigned char) *s < 128)
+ *(d++) = *s;
+
+ *d = 0;
+
+ return r;
+}
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
index 4d751953..b9f74952 100644
--- a/src/pulse/utf8.h
+++ b/src/pulse/utf8.h
@@ -36,9 +36,15 @@ PA_C_DECL_BEGIN
/** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */
char *pa_utf8_valid(const char *str) PA_GCC_PURE;
+/** Test if the specified strings qualifies as valid 7-bit ASCII. Return the string if so, otherwise NULL. \since 0.9.15 */
+char *pa_ascii_valid(const char *str) PA_GCC_PURE;
+
/** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */
char *pa_utf8_filter(const char *str);
+/** Filter all invalid ASCII characters from the specified string, returning a new fully ASCII valid string. Don't forget to free the returned string with pa_xfree(). \since 0.9.15 */
+char *pa_ascii_filter(const char *str);
+
/** Convert a UTF-8 string to the current locale. Free the string using pa_xfree(). */
char* pa_utf8_to_locale (const char *str);
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 01a28e83..54838e89 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -37,6 +37,9 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+ pa_return_val_if_fail(pa_cvolume_valid(b), 0);
+
if (a->channels != b->channels)
return 0;
@@ -78,7 +81,9 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) {
pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
uint64_t sum = 0;
int i;
+
pa_assert(a);
+ pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (i = 0; i < a->channels; i++)
sum += a->values[i];
@@ -91,7 +96,9 @@ pa_volume_t pa_cvolume_avg(const pa_cvolume *a) {
pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
pa_volume_t m = 0;
int i;
+
pa_assert(a);
+ pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
for (i = 0; i < a->channels; i++)
if (a->values[i] > m)
@@ -247,11 +254,12 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) {
return s;
}
-/** Return non-zero if the volume of all channels is equal to the specified value */
int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
unsigned c;
pa_assert(a);
+ pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+
for (c = 0; c < a->channels; c++)
if (a->values[c] != v)
return 0;
@@ -266,6 +274,9 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+ pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
+
for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]);
@@ -281,6 +292,9 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa
pa_assert(a);
pa_assert(b);
+ pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+ pa_return_val_if_fail(pa_cvolume_valid(b), NULL);
+
for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]);
@@ -373,7 +387,11 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa
pa_assert(v);
pa_assert(from);
pa_assert(to);
- pa_assert(v->channels == from->channels);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(pa_channel_map_valid(from), NULL);
+ pa_return_val_if_fail(pa_channel_map_valid(to), NULL);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL);
if (pa_channel_map_equal(from, to))
return v;
@@ -419,15 +437,22 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) {
pa_assert(v);
pa_assert(ss);
- if (!pa_cvolume_valid(v))
- return 0;
-
- if (!pa_sample_spec_valid(ss))
- return 0;
+ pa_return_val_if_fail(pa_cvolume_valid(v), 0);
+ pa_return_val_if_fail(pa_sample_spec_valid(ss), 0);
return v->channels == ss->channels;
}
+int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) {
+ pa_assert(v);
+ pa_assert(cm);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), 0);
+ pa_return_val_if_fail(pa_channel_map_valid(cm), 0);
+
+ return v->channels == cm->channels;
+}
+
static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r) {
int c;
pa_volume_t left = 0, right = 0;
@@ -465,7 +490,10 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
pa_assert(v);
pa_assert(map);
- pa_assert(map->channels == v->channels);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f);
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_balance(map))
return 0.0f;
@@ -494,12 +522,15 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo
pa_volume_t left, nleft, right, nright, m;
unsigned c;
- pa_assert(map->channels == v->channels);
pa_assert(map);
pa_assert(v);
pa_assert(new_balance >= -1.0f);
pa_assert(new_balance <= 1.0f);
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
+
if (!pa_channel_map_can_balance(map))
return v;
@@ -538,6 +569,9 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
pa_assert(v);
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(max != (pa_volume_t) -1, NULL);
+
for (c = 0; c < v->channels; c++)
if (v->values[c] > t)
t = v->values[c];
@@ -588,7 +622,10 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) {
pa_assert(v);
pa_assert(map);
- pa_assert(map->channels == v->channels);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f);
+ pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f);
if (!pa_channel_map_can_fade(map))
return 0.0f;
@@ -608,12 +645,15 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float
pa_volume_t front, nfront, rear, nrear, m;
unsigned c;
- pa_assert(map->channels == v->channels);
pa_assert(map);
pa_assert(v);
pa_assert(new_fade >= -1.0f);
pa_assert(new_fade <= 1.0f);
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(pa_channel_map_valid(map), NULL);
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL);
+
if (!pa_channel_map_can_fade(map))
return v;
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 9ad3e9bf..8bfd0687 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -230,10 +230,14 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
/** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */
pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to);
-/** Return non-zero if the specified volume is compatible with
- * the specified sample spec. \since 0.9.13 */
+/** Return non-zero if the specified volume is compatible with the
+ * specified sample spec. \since 0.9.13 */
int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE;
+/** Return non-zero if the specified volume is compatible with the
+ * specified sample spec. \since 0.9.15 */
+int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) PA_GCC_PURE;
+
/** Calculate a 'balance' value for the specified volume with the
* specified channel map. The return value will range from -1.0f
* (left) to +1.0f (right). If no balance value is applicable to this