summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2008-03-15 15:18:55 +0000
committerLennart Poettering <lennart@poettering.net>2008-03-15 15:18:55 +0000
commit347cfc356aa1c5073a5fc1d4355392759df13ab8 (patch)
treeb70f0e03673bde48cabba6e526915b4eabf96552 /src
parent8d9bdaca5a4092cd078d79c58c4bfa0da277bff4 (diff)
commit glitch-free work
git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2121 fefdeb5f-60dc-0310-8127-8f9354f1896f
Diffstat (limited to 'src')
-rw-r--r--src/pulse/context.c208
-rw-r--r--src/pulse/context.h24
-rw-r--r--src/pulse/def.h10
-rw-r--r--src/pulse/internal.h7
-rw-r--r--src/pulse/introspect.c46
-rw-r--r--src/pulse/introspect.h14
-rw-r--r--src/pulse/proplist.c66
-rw-r--r--src/pulse/proplist.h165
-rw-r--r--src/pulse/sample.c10
-rw-r--r--src/pulse/scache.c122
-rw-r--r--src/pulse/scache.h31
-rw-r--r--src/pulse/stream.c132
-rw-r--r--src/pulse/stream.h25
-rw-r--r--src/pulse/volume.h4
14 files changed, 765 insertions, 99 deletions
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 7243a29d..b9d93083 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -35,6 +35,7 @@
#include <errno.h>
#include <signal.h>
#include <limits.h>
+#include <locale.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
@@ -52,6 +53,8 @@
#include <pulse/version.h>
#include <pulse/xmalloc.h>
+#include <pulse/utf8.h>
+#include <pulse/util.h>
#include <pulsecore/winsock.h>
#include <pulsecore/core-error.h>
@@ -108,20 +111,32 @@ static void unlock_autospawn_lock_file(pa_context *c) {
static void context_free(pa_context *c);
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) {
+ return pa_context_new_with_proplist(mainloop, name, NULL);
+}
+
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
pa_context *c;
pa_assert(mainloop);
- pa_assert(name);
+
+ if (!name && !pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))
+ return NULL;
c = pa_xnew(pa_context, 1);
PA_REFCNT_INIT(c);
- c->name = pa_xstrdup(name);
+
+ c->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
+
c->mainloop = mainloop;
c->client = NULL;
c->pstream = NULL;
c->pdispatch = NULL;
c->playback_streams = pa_dynarray_new();
c->record_streams = pa_dynarray_new();
+ c->client_index = PA_INVALID_INDEX;
PA_LLIST_HEAD_INIT(pa_stream, c->streams);
PA_LLIST_HEAD_INIT(pa_operation, c->operations);
@@ -204,7 +219,9 @@ static void context_free(pa_context *c) {
pa_strlist_free(c->server_list);
- pa_xfree(c->name);
+ if (c->proplist)
+ pa_proplist_free(c->proplist);
+
pa_xfree(c->server);
pa_xfree(c);
}
@@ -415,7 +432,13 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
}
reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
- pa_tagstruct_puts(reply, c->name);
+
+ if (c->version >= 13) {
+ pa_init_proplist(c->proplist);
+ pa_tagstruct_put_proplist(reply, c->proplist);
+ } else
+ pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
+
pa_pstream_send_tagstruct(c->pstream, reply);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
@@ -424,11 +447,19 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
}
case PA_CONTEXT_SETTING_NAME :
+
+ if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
+ c->client_index == PA_INVALID_INDEX)) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
pa_context_set_state(c, PA_CONTEXT_READY);
break;
default:
- pa_assert(0);
+ pa_assert_not_reached();
}
finish:
@@ -987,12 +1018,19 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
- o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ if (c->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name);
+ o = pa_context_proplist_update(c, PA_UPDATE_REPLACE, p, cb, userdata);
+ pa_proplist_free(p);
- t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
- pa_tagstruct_puts(t, name);
- 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);
+ } else {
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+ t = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
+ pa_tagstruct_puts(t, name);
+ 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;
}
@@ -1024,6 +1062,8 @@ uint32_t pa_context_get_server_protocol_version(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+
return c->version;
}
@@ -1039,3 +1079,151 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
return t;
}
+
+uint32_t pa_context_get_index(pa_context *c) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_ANY(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE, PA_INVALID_INDEX);
+ PA_CHECK_VALIDITY_RETURN_ANY(c, c->version >= 13, PA_ERR_NOTSUPPORTED, PA_INVALID_INDEX);
+
+ return c->client_index;
+}
+
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_UPDATE_CLIENT_PROPLIST, &tag);
+ pa_tagstruct_putu32(t, (uint32_t) mode);
+ pa_tagstruct_put_proplist(t, p);
+
+ 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);
+
+ /* Please note that we don't update c->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ const char * const *k;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, keys && keys[0], PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_CLIENT_PROPLIST, &tag);
+
+ for (k = keys; *k; k++)
+ pa_tagstruct_puts(t, *k);
+
+ pa_tagstruct_puts(t, NULL);
+
+ 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);
+
+ /* Please note that we don't update c->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+void pa_init_proplist(pa_proplist *p) {
+ int a, b;
+#ifndef HAVE_DECL_ENVIRON
+ extern char **environ;
+#endif
+ char **e;
+
+ pa_assert(p);
+
+ for (e = environ; *e; e++) {
+
+ if (pa_startswith(*e, "PULSE_PROP_")) {
+ size_t kl = strcspn(*e+11, "=");
+ char *k;
+
+ if ((*e)[11+kl] != '=')
+ continue;
+
+ if (!pa_utf8_valid(*e+11+kl+1))
+ continue;
+
+ 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 (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) {
+ char t[32];
+ pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid());
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_ID, t);
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_USER)) {
+ char t[64];
+ if (pa_get_user_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_USER, c);
+ pa_xfree(c);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_HOST)) {
+ char t[64];
+ if (pa_get_host_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_HOST, c);
+ pa_xfree(c);
+ }
+ }
+
+ if (!(a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY)) ||
+ !(b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))) {
+ char t[PATH_MAX];
+ if (pa_get_binary_name(t, sizeof(t))) {
+ char *c = pa_utf8_filter(t);
+
+ if (!a)
+ pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
+ if (!b)
+ pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, c);
+
+ pa_xfree(c);
+ }
+ }
+
+ if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
+ const char *l;
+
+ if ((l = setlocale(LC_MESSAGES, NULL)))
+ pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
+ }
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
index 1de3abad..cb2a531f 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -30,6 +30,7 @@
#include <pulse/mainloop-api.h>
#include <pulse/cdecl.h>
#include <pulse/operation.h>
+#include <pulse/proplist.h>
/** \page async Asynchronous API
*
@@ -166,9 +167,15 @@ typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
/** Instantiate a new connection context with an abstract mainloop API
- * and an application name */
+ * and an application name. It is recommended to use pa_context_new_with_proplist()
+ * instead and specify some initial properties.*/
pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name);
+/** Instantiate a new connection context with an abstract mainloop API
+ * and an application name, and specify the the initial client property
+ * list. \since 0.9.10 */
+pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *proplist);
+
/** Decrease the reference counter of the context by one */
void pa_context_unref(pa_context *c);
@@ -228,6 +235,21 @@ uint32_t pa_context_get_protocol_version(pa_context *c);
/** Return the protocol version of the connected server. \since 0.8 */
uint32_t pa_context_get_server_protocol_version(pa_context *c);
+/* Update the property list of the client, adding new entries. Please
+ * note that it is highly recommended to set as much properties
+ * initially via pa_context_new_with_proplist() as possible instead a
+ * posteriori with this function, since that information may then be
+ * used to route streams of the client to the right device. \since 0.9.10 */
+pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata);
+
+/* Update the property list of the client, remove entries. \since 0.9.10 */
+pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata);
+
+/** Return the client index this context is
+ * identified in the server with. This is useful for usage with the
+ * introspection functions, such as pa_context_get_client_info(). \since 0.9.10 */
+uint32_t pa_context_get_index(pa_context *s);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
index dabbc5eb..edda792a 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -209,6 +209,8 @@ typedef enum pa_stream_flags {
* least PA 0.9.8. It is ignored
* on older servers. \since
* 0.9.8 */
+ PA_STREAM_PEAK_DETECT = 2048, /**< Find peaks instead of
+ * resampling. \since 0.9.9 */
} pa_stream_flags_t;
/** Playback and record buffer metrics */
@@ -378,7 +380,9 @@ typedef enum pa_sink_flags {
PA_SINK_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
PA_SINK_LATENCY = 2, /**< Supports latency querying */
PA_SINK_HARDWARE = 4, /**< Is a hardware sink of some kind, in contrast to "virtual"/software sinks \since 0.9.3 */
- PA_SINK_NETWORK = 8 /**< Is a networked sink of some kind. \since 0.9.7 */
+ PA_SINK_NETWORK = 8, /**< Is a networked sink of some kind. \since 0.9.7 */
+ PA_SINK_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.10 */
+ PA_SINK_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.10 */
} pa_sink_flags_t;
/** Special source flags. \since 0.8 */
@@ -386,7 +390,9 @@ typedef enum pa_source_flags {
PA_SOURCE_HW_VOLUME_CTRL = 1, /**< Supports hardware volume control */
PA_SOURCE_LATENCY = 2, /**< Supports latency querying */
PA_SOURCE_HARDWARE = 4, /**< Is a hardware source of some kind, in contrast to "virtual"/software source \since 0.9.3 */
- PA_SOURCE_NETWORK = 8 /**< Is a networked sink of some kind. \since 0.9.7 */
+ PA_SOURCE_NETWORK = 8, /**< Is a networked sink of some kind. \since 0.9.7 */
+ PA_SOURCE_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.10 */
+ PA_SOURCE_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.10 */
} pa_source_flags_t;
/** A generic free() like callback prototype */
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 873f1363..d988e1a7 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -50,7 +50,7 @@
struct pa_context {
PA_REFCNT_DECLARE;
- char *name;
+ pa_proplist *proplist;
pa_mainloop_api* mainloop;
pa_socket_client *client;
@@ -85,6 +85,8 @@ struct pa_context {
char *server;
pa_client_conf *conf;
+
+ uint32_t client_index;
};
#define PA_MAX_WRITE_INDEX_CORRECTIONS 10
@@ -102,7 +104,7 @@ struct pa_stream {
pa_mainloop_api *mainloop;
PA_LLIST_FIELDS(pa_stream);
- char *name;
+ pa_proplist *proplist;
pa_bool_t manual_buffer_attr;
pa_buffer_attr buffer_attr;
pa_sample_spec sample_spec;
@@ -226,5 +228,6 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
+void pa_init_proplist(pa_proplist *p);
#endif
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 6610a724..4b282bdb 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -149,7 +149,9 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
while (!pa_tagstruct_eof(t)) {
pa_sink_info i;
+
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -163,9 +165,11 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
pa_tagstruct_gets(t, &i.monitor_source_name) < 0 ||
pa_tagstruct_get_usec(t, &i.latency) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
- pa_tagstruct_getu32(t, &flags) < 0) {
+ pa_tagstruct_getu32(t, &flags) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -175,6 +179,8 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P
pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -260,7 +266,9 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_source_info i;
uint32_t flags;
+
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -274,9 +282,11 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 ||
pa_tagstruct_get_usec(t, &i.latency) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
- pa_tagstruct_getu32(t, &flags) < 0) {
+ pa_tagstruct_getu32(t, &flags) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -286,6 +296,8 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -370,13 +382,18 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
while (!pa_tagstruct_eof(t)) {
pa_client_info i;
+
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
- pa_tagstruct_gets(t, &i.driver) < 0 ) {
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
+
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -384,6 +401,8 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command,
pa_client_info_cb_t cb = (pa_client_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -521,7 +540,9 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
while (!pa_tagstruct_eof(t)) {
pa_sink_input_info i;
+
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -535,9 +556,11 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
pa_tagstruct_get_usec(t, &i.sink_usec) < 0 ||
pa_tagstruct_gets(t, &i.resample_method) < 0 ||
pa_tagstruct_gets(t, &i.driver) < 0 ||
- (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &i.mute) < 0)) {
+ (o->context->version >= 11 && pa_tagstruct_get_boolean(t, &i.mute) < 0) ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -545,6 +568,8 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -608,6 +633,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_source_output_info i;
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -619,9 +645,11 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_tagstruct_get_usec(t, &i.buffer_usec) < 0 ||
pa_tagstruct_get_usec(t, &i.source_usec) < 0 ||
pa_tagstruct_gets(t, &i.resample_method) < 0 ||
- pa_tagstruct_gets(t, &i.driver) < 0) {
+ pa_tagstruct_gets(t, &i.driver) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ pa_proplist_free(i.proplist);
goto finish;
}
@@ -629,6 +657,8 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
@@ -933,6 +963,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
pa_sample_info i;
memset(&i, 0, sizeof(i));
+ i.proplist = pa_proplist_new();
if (pa_tagstruct_getu32(t, &i.index) < 0 ||
pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -942,7 +973,8 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
pa_tagstruct_getu32(t, &i.bytes) < 0 ||
pa_tagstruct_get_boolean(t, &i.lazy) < 0 ||
- pa_tagstruct_gets(t, &i.filename) < 0) {
+ pa_tagstruct_gets(t, &i.filename) < 0 ||
+ (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
goto finish;
@@ -952,6 +984,8 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command,
pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
+
+ pa_proplist_free(i.proplist);
}
}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index c148ee5e..91f738df 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -32,6 +32,7 @@
#include <pulse/cdecl.h>
#include <pulse/channelmap.h>
#include <pulse/volume.h>
+#include <pulse/proplist.h>
/** \page introspect Server Query and Control
*
@@ -206,6 +207,11 @@
PA_C_DECL_BEGIN
+#define PA_PORT_SPDIF "spdif"
+#define PA_PORT_ANALOG_STEREO "analog-stereo"
+#define PA_PORT_ANALOG_5_1 "analog-5-1"
+#define PA_PORT_ANALOG_4_0 "analog-4-0"
+
/** Stores information about sinks */
typedef struct pa_sink_info {
const char *name; /**< Name of the sink */
@@ -221,6 +227,7 @@ typedef struct pa_sink_info {
pa_usec_t latency; /**< Length of filled playback buffer of this sink */
const char *driver; /**< Driver name. \since 0.8 */
pa_sink_flags_t flags; /**< Flags \since 0.8 */
+ pa_proplist *proplist; /**< Property list \since 0.9.10 */
} pa_sink_info;
/** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@@ -237,7 +244,7 @@ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb,
/** Stores information about sources */
typedef struct pa_source_info {
- const char *name ; /**< Name of the source */
+ const char *name; /**< Name of the source */
uint32_t index; /**< Index of the source */
const char *description; /**< Description of this source */
pa_sample_spec sample_spec; /**< Sample spec of this source */
@@ -250,6 +257,7 @@ typedef struct pa_source_info {
pa_usec_t latency; /**< Length of filled record buffer of this source. \since 0.5 */
const char *driver; /**< Driver name \since 0.8 */
pa_source_flags_t flags; /**< Flags \since 0.8 */
+ pa_proplist *proplist; /**< Property list \since 0.9.10 */
} pa_source_info;
/** Callback prototype for pa_context_get_source_info_by_name() and friends */
@@ -306,6 +314,7 @@ typedef struct pa_client_info {
const char *name; /**< Name of this client */
uint32_t owner_module; /**< Index of the owning module, or PA_INVALID_INDEX */
const char *driver; /**< Driver name \since 0.8 */
+ pa_proplist *proplist; /**< Property list \since 0.9.10 */
} pa_client_info;
/** Callback prototype for pa_context_get_client_info() and firends*/
@@ -332,6 +341,7 @@ typedef struct pa_sink_input_info {
const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */
const char *driver; /**< Driver name \since 0.8 */
int mute; /**< Stream muted \since 0.9.7 */
+ pa_proplist *proplist; /**< Property list \since 0.9.10 */
} pa_sink_input_info;
/** Callback prototype for pa_context_get_sink_input_info() and firends*/
@@ -356,6 +366,7 @@ typedef struct pa_source_output_info {
pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. \since 0.5 */
const char *resample_method; /**< Thre resampling method used by this source output. \since 0.7 */
const char *driver; /**< Driver name \since 0.8 */
+ pa_proplist *proplist; /**< Property list \since 0.9.10 */
} pa_source_output_info;
/** Callback prototype for pa_context_get_source_output_info() and firends*/
@@ -423,6 +434,7 @@ typedef struct pa_sample_info {
uint32_t bytes; /**< Length of this sample in bytes. \since 0.4 */
int lazy; /**< Non-zero when this is a lazy cache entry. \since 0.5 */
const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. \since 0.5 */
+ pa_proplist *proplist; /**< Property list for this sample. \since 0.9.10 */
} pa_sample_info;
/** Callback prototype for pa_context_get_sample_info_by_name() and firends */
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index c27c9d84..31fd10d5 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -69,16 +69,14 @@ pa_proplist* pa_proplist_new(void) {
}
void pa_proplist_free(pa_proplist* p) {
- struct property *prop;
-
- while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
- property_free(prop);
+ pa_assert(p);
+ pa_proplist_clear(p);
pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL);
}
/** Will accept only valid UTF-8 */
-int pa_proplist_puts(pa_proplist *p, const char *key, const char *value) {
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
struct property *prop;
pa_bool_t add = FALSE;
@@ -104,7 +102,7 @@ int pa_proplist_puts(pa_proplist *p, const char *key, const char *value) {
return 0;
}
-int pa_proplist_put(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
struct property *prop;
pa_bool_t add = FALSE;
@@ -175,18 +173,27 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *
return 0;
}
-void pa_proplist_merge(pa_proplist *p, pa_proplist *other) {
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other) {
struct property *prop;
void *state = NULL;
pa_assert(p);
+ pa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE);
pa_assert(other);
- while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL)))
- pa_assert_se(pa_proplist_put(p, prop->key, prop->value, prop->nbytes) == 0);
+ if (mode == PA_UPDATE_SET)
+ pa_proplist_clear(p);
+
+ while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL))) {
+
+ if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, prop->key))
+ continue;
+
+ pa_assert_se(pa_proplist_set(p, prop->key, prop->value, prop->nbytes) == 0);
+ }
}
-int pa_proplist_remove(pa_proplist *p, const char *key) {
+int pa_proplist_unset(pa_proplist *p, const char *key) {
struct property *prop;
pa_assert(p);
@@ -196,12 +203,30 @@ int pa_proplist_remove(pa_proplist *p, const char *key) {
return -1;
if (!(prop = pa_hashmap_remove(MAKE_HASHMAP(p), key)))
- return -1;
+ return -2;
property_free(prop);
return 0;
}
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) {
+ const char * const * k;
+ int n = 0;
+
+ pa_assert(p);
+ pa_assert(keys);
+
+ for (k = keys; *k; k++)
+ if (!property_name_valid(*k))
+ return -1;
+
+ for (k = keys; *k; k++)
+ if (pa_proplist_unset(p, *k) >= 0)
+ n++;
+
+ return n;
+}
+
const char *pa_proplist_iterate(pa_proplist *p, void **state) {
struct property *prop;
@@ -255,3 +280,22 @@ int pa_proplist_contains(pa_proplist *p, const char *key) {
return 1;
}
+
+void pa_proplist_clear(pa_proplist *p) {
+ struct property *prop;
+ pa_assert(p);
+
+ while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
+ property_free(prop);
+}
+
+pa_proplist* pa_proplist_copy(pa_proplist *template) {
+ pa_proplist *p;
+
+ pa_assert_se(p = pa_proplist_new());
+
+ if (template)
+ pa_proplist_update(p, PA_UPDATE_REPLACE, template);
+
+ return p;
+}
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index c4cf9ac9..4fea23da 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -28,63 +28,154 @@
/* Defined properties:
*
- * x11.xid
- * x11.display
- * x11.x_pointer
- * x11.y_pointer
- * x11.button
- * media.name
- * media.title
- * media.artist
- * media.language
+ * media.name "Guns'N'Roses: Civil War"
+ * media.title "Civil War"
+ * media.artist "Guns'N'Roses"
+ * media.language "de_DE"
* media.filename
* media.icon
* media.icon_name
- * media.role video, music, game, event, phone, production
- * application.name
+ * media.role video, music, game, event, phone, production, routing, abstract
+ * event.id button-click, session-login
+ * event.x11.display
+ * event.x11.xid
+ * event.x11.x_pointer
+ * event.x11.y_pointer
+ * event.x11.button
+ * application.name "Rhythmbox Media Player"
+ * application.id "org.gnome.rhythmbox"
* application.version
* application.icon
* application.icon_name
+ * application.process.id
+ * application.process.binary
+ * application.process.user
+ * application.process.host
+ * device.string
+ * device.api oss, alsa, sunaudio
+ * device.description
+ * device.bus_path
+ * device.serial
+ * device.vendor_product_id
+ * device.class sound, modem, monitor
+ * device.form_factor laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset
+ * device.connector isa, pci, usb, firewire, bluetooth
+ * device.access_mode mmap, mmap_rewrite, serial
+ * device.master_device
+ * device.buffer_size
*/
-#define PA_PROP_X11_XID "x11.xid"
-#define PA_PROP_X11_DISPLAY "x11.display"
-#define PA_PROP_X11_X_POINTER "x11.x_pointer"
-#define PA_PROP_X11_Y_POINTER "x11.y_pointer"
-#define PA_PROP_X11_BUTTON "x11.button"
-#define PA_PROP_MEDIA_NAME "media.name"
-#define PA_PROP_MEDIA_TITLE "media.title"
-#define PA_PROP_MEDIA_ARTIST "media.artist"
-#define PA_PROP_MEDIA_LANGUAGE "media.language"
-#define PA_PROP_MEDIA_FILENAME "media.filename"
-#define PA_PROP_MEDIA_ICON "media.icon"
-#define PA_PROP_MEDIA_ICON_NAME "media.icon_name"
-#define PA_PROP_MEDIA_ROLE "media.role"
-#define PA_PROP_APPLICATION_NAME "application.name"
-#define PA_PROP_APPLICATION_VERSION "application.version"
-#define PA_PROP_APPLICATION_ICON "application.icon"
-#define PA_PROP_APPLICATION_ICON_NAME "application.icon_name"
-
+#define PA_PROP_MEDIA_NAME "media.name"
+#define PA_PROP_MEDIA_TITLE "media.title"
+#define PA_PROP_MEDIA_ARTIST "media.artist"
+#define PA_PROP_MEDIA_LANGUAGE "media.language"
+#define PA_PROP_MEDIA_FILENAME "media.filename"
+#define PA_PROP_MEDIA_ICON "media.icon"
+#define PA_PROP_MEDIA_ICON_NAME "media.icon_name"
+#define PA_PROP_MEDIA_ROLE "media.role"
+#define PA_PROP_EVENT_ID "event.id"
+#define PA_PROP_EVENT_X11_DISPLAY "event.x11.display"
+#define PA_PROP_EVENT_X11_XID "event.x11.xid"
+#define PA_PROP_EVENT_MOUSE_X "event.mouse.x"
+#define PA_PROP_EVENT_MOUSE_Y "event.mouse.y"
+#define PA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button"
+#define PA_PROP_APPLICATION_NAME "application.name"
+#define PA_PROP_APPLICATION_ID "application.id"
+#define PA_PROP_APPLICATION_VERSION "application.version"
+#define PA_PROP_APPLICATION_ICON "application.icon"
+#define PA_PROP_APPLICATION_ICON_NAME "application.icon_name"
+#define PA_PROP_APPLICATION_LANGUAGE "application.language"
+#define PA_PROP_APPLICATION_PROCESS_ID "application.process.id"
+#define PA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary"
+#define PA_PROP_APPLICATION_PROCESS_USER "application.process.user"
+#define PA_PROP_APPLICATION_PROCESS_HOST "application.process.host"
+#define PA_PROP_DEVICE_STRING "device.string"
+#define PA_PROP_DEVICE_API "device.api"
+#define PA_PROP_DEVICE_DESCRIPTION "device.description"
+#define PA_PROP_DEVICE_BUS_PATH "device.bus_path"
+#define PA_PROP_DEVICE_SERIAL "device.serial"
+#define PA_PROP_DEVICE_VENDOR_PRODUCT_ID "device.vendor_product_id"
+#define PA_PROP_DEVICE_CLASS "device.class"
+#define PA_PROP_DEVICE_FORM_FACTOR "device.form_factor"
+#define PA_PROP_DEVICE_CONNECTOR "device.connector"
+#define PA_PROP_DEVICE_ACCESS_MODE "device.access_mode"
+#define PA_PROP_DEVICE_MASTER_DEVICE "device.master_device"
+
+/** A property list object. Basically a dictionary with UTF-8 strings
+ * as keys and arbitrary data as values. \since 0.9.10 */
typedef struct pa_proplist pa_proplist;
+/** Allocate a property list. \since 0.9.10 */
pa_proplist* pa_proplist_new(void);
-void pa_proplist_free(pa_proplist* p);
-/** Will accept only valid UTF-8 */
-int pa_proplist_puts(pa_proplist *p, const char *key, const char *value);
-int pa_proplist_put(pa_proplist *p, const char *key, const void *data, size_t nbytes);
+/** Free the property list. \since 0.9.10 */
+void pa_proplist_free(pa_proplist* p);
-/* Will return NULL if the data is not valid UTF-8 */
+/** Append a new string entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. Will accept only valid
+ * UTF-8. \since 0.9.10 */
+int pa_proplist_sets(pa_proplist *p, const char *key, const char *value);
+
+/** Append a new arbitrary data entry to the property list, possibly
+ * overwriting an already existing entry with the same key. An
+ * internal copy of the data passed is made. \since 0.9.10 */
+int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes);
+
+/* Return a string entry for the specified key. Will return NULL if
+ * the data is not valid UTF-8. Will return a NUL-terminated string in
+ * an internally allocated buffer. The caller should make a copy of
+ * the data before accessing the property list again. \since 0.9.10*/
const char *pa_proplist_gets(pa_proplist *p, const char *key);
-int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes);
-void pa_proplist_merge(pa_proplist *p, pa_proplist *other);
-int pa_proplist_remove(pa_proplist *p, const char *key);
+/** Return the the value for the specified key. Will return a
+ * NUL-terminated string for string entries. The pointer returned will
+ * point to an internally allocated buffer. The caller should make a
+ * copy of the data before the property list is accessed again. \since 0.9.10 */
+int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes);
+/** Update mode enum for pa_proplist_update(). \since 0.9.10 */
+typedef enum pa_update_mode {
+ PA_UPDATE_SET, /*< Replace the entirey property list with the new one. Don't keep any of the old data around */
+ PA_UPDATE_MERGE, /*< Merge new property list into the existing one, not replacing any old entries if they share a common key with the new property list. */
+ PA_UPDATE_REPLACE /*< Merge new property list into the existing one, replacing all old entries that share a common key with the new property list. */
+} pa_update_mode_t;
+
+/** Merge property list "other" into "p", adhering the merge mode as
+ * specified in "mode". \since 0.9.10 */
+void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other);
+
+/** Removes a single entry from the property list, identified be the
+ * specified key name. \since 0.9.10 */
+int pa_proplist_unset(pa_proplist *p, const char *key);
+
+/** Similar to pa_proplist_remove() but takes an array of keys to
+ * remove. The array should be terminated by a NULL pointer. Return -1
+ * on failure, otherwise the number of entries actually removed (which
+ * might even be 0, if there where no matching entries to
+ * remove). \since 0.9.10 */
+int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]);
+
+/** Iterate through the property list. The user should allocate a
+ * state variable of type void* and initialize it with NULL. A pointer
+ * to this variable should then be passed to pa_proplist_iterate()
+ * which should be called in a loop until it returns NULL which
+ * signifies EOL. The property list should not be modified during
+ * iteration through the list. On each invication this function will
+ * return the key string for the next entry. The keys in the property
+ * list do not have any particular order. \since 0.9.10 */
const char *pa_proplist_iterate(pa_proplist *p, void **state);
+/** Format the property list nicely as a human readable string. \since 0.9.10 */
char *pa_proplist_to_string(pa_proplist *p);
+/** Returns 1 if an entry for the specified key is existant in the property list. \since 0.9.10 */
int pa_proplist_contains(pa_proplist *p, const char *key);
+/** Remove all entries from the property list object. \since 0.9.10 */
+void pa_proplist_clear(pa_proplist *p);
+
+/** Allocate a new property list and copy over every single entry from the specific list. \since 0.9.10 */
+pa_proplist* pa_proplist_copy(pa_proplist *template);
+
#endif
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 27c0df03..43340f20 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -32,6 +32,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
+#include <pulse/timeval.h>
#include "sample.h"
@@ -70,13 +71,13 @@ size_t pa_bytes_per_second(const pa_sample_spec *spec) {
pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) {
pa_assert(spec);
- return (pa_usec_t) (((double) length/pa_frame_size(spec)*1000000)/spec->rate);
+ 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);
- return (size_t) (((double) t * spec->rate / 1000000))*pa_frame_size(spec);
+ return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec);
}
int pa_sample_spec_valid(const pa_sample_spec *spec) {
@@ -97,7 +98,10 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
pa_assert(a);
pa_assert(b);
- return (a->format == b->format) && (a->rate == b->rate) && (a->channels == b->channels);
+ return
+ (a->format == b->format) &&
+ (a->rate == b->rate) &&
+ (a->channels == b->channels);
}
const char *pa_sample_format_to_string(pa_sample_format_t f) {
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index 186b0a3e..24f340ea 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -49,12 +49,22 @@ int pa_stream_connect_upload(pa_stream *s, size_t length) {
pa_stream_ref(s);
s->direction = PA_STREAM_UPLOAD;
+ s->flags = 0;
t = pa_tagstruct_command(s->context, PA_COMMAND_CREATE_UPLOAD_STREAM, &tag);
- pa_tagstruct_puts(t, s->name);
+
+ if (s->context->version < 13)
+ pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME));
+
pa_tagstruct_put_sample_spec(t, &s->sample_spec);
pa_tagstruct_put_channel_map(t, &s->channel_map);
pa_tagstruct_putu32(t, length);
+
+ if (s->context->version >= 13) {
+ pa_init_proplist(s->proplist);
+ pa_tagstruct_put_proplist(t, s->proplist);
+ }
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
@@ -85,6 +95,73 @@ int pa_stream_finish_upload(pa_stream *s) {
return 0;
}
+static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ int success = 1;
+ uint32_t idx = PA_INVALID_INDEX;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ success = 0;
+ } else if ((o->context->version >= 13 && pa_tagstruct_getu32(t, &idx) < 0) ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ } else if (o->context->version >= 13 && idx == PA_INVALID_INDEX)
+ success = 0;
+
+ if (o->callback) {
+ pa_context_success_cb_t cb = (pa_context_success_cb_t) o->callback;
+ cb(o->context, success, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_operation *o = userdata;
+ uint32_t idx;
+
+ pa_assert(pd);
+ pa_assert(o);
+ pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+ if (!o->context)
+ goto finish;
+
+ if (command != PA_COMMAND_REPLY) {
+ if (pa_context_handle_error(o->context, command, t) < 0)
+ goto finish;
+
+ idx = PA_INVALID_INDEX;
+ } else if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (o->callback) {
+ pa_context_play_sample_cb_t cb = (pa_context_play_sample_cb_t) o->callback;
+ cb(o->context, idx, o->userdata);
+ }
+
+finish:
+ pa_operation_done(o);
+ pa_operation_unref(o);
+}
+
+
pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_context_success_cb_t cb, void *userdata) {
pa_operation *o;
pa_tagstruct *t;
@@ -107,8 +184,47 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char
pa_tagstruct_puts(t, dev);
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
+
+ if (c->version >= 13) {
+ pa_proplist *p = pa_proplist_new();
+ pa_tagstruct_put_proplist(t, p);
+ pa_proplist_free(p);
+ }
+
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);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ return o;
+}
+
+pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *name, const char *dev, pa_volume_t volume, pa_proplist *p, pa_context_play_sample_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, p, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+ if (!dev)
+ dev = c->conf->default_sink;
+
+ t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
+ pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+ pa_tagstruct_puts(t, dev);
+ pa_tagstruct_putu32(t, volume);
+ pa_tagstruct_puts(t, name);
+ pa_tagstruct_put_proplist(t, p);
+
+ pa_pstream_send_tagstruct(c->pstream, t);
+ pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_with_proplist_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
}
@@ -128,9 +244,9 @@ pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_conte
t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_SAMPLE, &tag);
pa_tagstruct_puts(t, name);
+
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;
}
-
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
index 31fd8956..a9e0ce8c 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -79,14 +79,25 @@
PA_C_DECL_BEGIN
+/** Callback prototype for pa_context_play_sample_with_proplist(). The
+ * idx value is the index of the sink input object, or
+ * PA_INVALID_INDEX on failure. \since 0.9.10*/
+typedef void (*pa_context_play_sample_cb_t)(pa_context *c, uint32_t idx, void *userdata);
+
/** Make this stream a sample upload stream */
int pa_stream_connect_upload(pa_stream *s, size_t length);
-/** Finish the sample upload, the stream name will become the sample name. You cancel a samp
- * le upload by issuing pa_stream_disconnect() */
+/** Finish the sample upload, the stream name will become the sample
+ * name. You cancel a sample upload by issuing
+ * pa_stream_disconnect() */
int pa_stream_finish_upload(pa_stream *s);
-/** Play a sample from the sample cache to the specified device. If the latter is NULL use the default sink. Returns an operation object */
+/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
+pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata);
+
+/** Play a sample from the sample cache to the specified device. If
+ * the latter is NULL use the default sink. Returns an operation
+ * object */
pa_operation* pa_context_play_sample(
pa_context *c /**< Context */,
const char *name /**< Name of the sample to play */,
@@ -95,8 +106,18 @@ pa_operation* pa_context_play_sample(
pa_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
void *userdata /**< Userdata to pass to the callback */);
-/** Remove a sample from the sample cache. Returns an operation object which may be used to cancel the operation while it is running */
-pa_operation* pa_context_remove_sample(pa_context *c, const char *name, pa_context_success_cb_t, void *userdata);
+/** Play a sample from the sample cache to the specified device,
+ * allowing specification of a property list for the playback
+ * stream. If the latter is NULL use the default sink. Returns an
+ * operation object. \since 0.9.10 */
+pa_operation* pa_context_play_sample_with_proplist(
+ pa_context *c /**< Context */,
+ const char *name /**< Name of the sample to play */,
+ const char *dev /**< Sink to play this sample on */,
+ pa_volume_t volume /**< Volume to play this sample with */ ,
+ pa_proplist *proplist /**< Property list for this sound. The property list of the cached entry will be merged into this property list */,
+ pa_context_play_sample_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
+ void *userdata /**< Userdata to pass to the callback */);
PA_C_DECL_END
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index c44323fc..321f2228 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -44,6 +44,10 @@
#define LATENCY_IPOL_INTERVAL_USEC (100000L)
pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map) {
+ return pa_stream_new_with_proplist(c, name, ss, map, NULL);
+}
+
+pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_proplist *p) {
pa_stream *s;
int i;
pa_channel_map tmap;
@@ -54,6 +58,7 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE || ss->format != PA_SAMPLE_S32NE), PA_ERR_NOTSUPPORTED);
PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(c, name || pa_proplist_contains(p, PA_PROP_MEDIA_NAME), PA_ERR_INVALID);
if (!map)
PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
@@ -83,11 +88,15 @@ pa_stream *pa_stream_new(pa_context *c, const char *name, const pa_sample_spec *
s->suspended_userdata = NULL;
s->direction = PA_STREAM_NODIRECTION;
- s->name = pa_xstrdup(name);
s->sample_spec = *ss;
s->channel_map = *map;
s->flags = 0;
+ s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new();
+
+ if (name)
+ pa_proplist_sets(c->proplist, PA_PROP_MEDIA_NAME, name);
+
s->channel = 0;
s->channel_valid = 0;
s->syncid = c->csyncid++;
@@ -151,7 +160,9 @@ static void stream_free(pa_stream *s) {
if (s->record_memblockq)
pa_memblockq_free(s->record_memblockq);
- pa_xfree(s->name);
+ if (s->proplist)
+ pa_proplist_free(s->proplist);
+
pa_xfree(s->device_name);
pa_xfree(s);
}
@@ -653,6 +664,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED
pa_frame_size(&s->sample_spec),
1,
0,
+ 0,
NULL);
}
@@ -692,19 +704,29 @@ static int create_stream(
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
+ pa_assert(direction == PA_STREAM_PLAYBACK || direction == PA_STREAM_RECORD);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_UNCONNECTED, PA_ERR_BADSTATE);
- PA_CHECK_VALIDITY(s->context, !(flags & ~((direction != PA_STREAM_UPLOAD ?
- PA_STREAM_START_CORKED|
- PA_STREAM_INTERPOLATE_TIMING|
- PA_STREAM_NOT_MONOTONOUS|
- PA_STREAM_AUTO_TIMING_UPDATE|
- PA_STREAM_NO_REMAP_CHANNELS|
- PA_STREAM_NO_REMIX_CHANNELS|
- PA_STREAM_FIX_FORMAT|
- PA_STREAM_FIX_RATE|
- PA_STREAM_FIX_CHANNELS|
- PA_STREAM_DONT_MOVE : 0))), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|
+ PA_STREAM_INTERPOLATE_TIMING|
+ PA_STREAM_NOT_MONOTONOUS|
+ PA_STREAM_AUTO_TIMING_UPDATE|
+ PA_STREAM_NO_REMAP_CHANNELS|
+ PA_STREAM_NO_REMIX_CHANNELS|
+ PA_STREAM_FIX_FORMAT|
+ PA_STREAM_FIX_RATE|
+ PA_STREAM_FIX_CHANNELS|
+ PA_STREAM_DONT_MOVE|
+ PA_STREAM_VARIABLE_RATE|
+ PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
+
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
+ PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
+ /* Althought some of the other flags are not supported on older
+ * version, we don't check for them here, because it doesn't hurt
+ * when they are passed but actually not supported. This makes
+ * client development easier */
+
PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
@@ -737,9 +759,11 @@ static int create_stream(
s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_CREATE_PLAYBACK_STREAM : PA_COMMAND_CREATE_RECORD_STREAM,
&tag);
+ if (s->context->version < 13)
+ pa_tagstruct_puts(t, pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME));
+
pa_tagstruct_put(
t,
- PA_TAG_STRING, s->name,
PA_TAG_SAMPLE_SPEC, &s->sample_spec,
PA_TAG_CHANNEL_MAP, &s->channel_map,
PA_TAG_U32, PA_INVALID_INDEX,
@@ -766,7 +790,7 @@ static int create_stream(
} else
pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
- if (s->context->version >= 12 && s->direction != PA_STREAM_UPLOAD) {
+ if (s->context->version >= 12) {
pa_tagstruct_put(
t,
PA_TAG_BOOLEAN, flags & PA_STREAM_NO_REMAP_CHANNELS,
@@ -779,6 +803,17 @@ static int create_stream(
PA_TAG_INVALID);
}
+ if (s->context->version >= 13) {
+
+ pa_init_proplist(s->proplist);
+
+ pa_tagstruct_put(
+ t,
+ PA_TAG_BOOLEAN, flags & PA_STREAM_PEAK_DETECT,
+ PA_TAG_PROPLIST, s->proplist,
+ PA_TAG_INVALID);
+ }
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
@@ -1835,5 +1870,72 @@ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_strea
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_update_sample_rate_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
return o;
+}
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST : PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+ pa_tagstruct_putu32(t, (uint32_t) mode);
+ pa_tagstruct_put_proplist(t, p);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ /* Please note that we don't update s->proplist here, because we
+ * don't export that field */
+
+ return o;
+}
+
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata) {
+ pa_operation *o;
+ pa_tagstruct *t;
+ uint32_t tag;
+ const char * const*k;
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, keys && keys[0], PA_ERR_INVALID);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 13, PA_ERR_NOTSUPPORTED);
+
+ o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata);
+
+ t = pa_tagstruct_command(
+ s->context,
+ s->direction == PA_STREAM_RECORD ? PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST : PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
+ &tag);
+ pa_tagstruct_putu32(t, s->channel);
+
+ for (k = keys; *k; k++)
+ pa_tagstruct_puts(t, *k);
+
+ pa_tagstruct_puts(t, NULL);
+
+ pa_pstream_send_tagstruct(s->context->pstream, t);
+ pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+ /* Please note that we don't update s->proplist here, because we
+ * don't export that field */
+
+ return o;
}
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 85473227..de7c967b 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -276,13 +276,25 @@ 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);
-/** Create a new, unconnected stream with the specified name and sample type */
+/** 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. */
pa_stream* pa_stream_new(
pa_context *c /**< The context to create this stream in */,
const char *name /**< A name for this stream */,
const pa_sample_spec *ss /**< The desired sample format */,
const pa_channel_map *map /**< The desired channel map, or NULL for default */);
+/** Create a new, unconnected stream with the specified name and
+ * sample type, and specify the the initial stream property
+ * list. \since 0.9.10 */
+pa_stream* pa_stream_new_with_proplist(
+ pa_context *c /**< The context to create this stream in */,
+ const char *name /**< A name for this stream */,
+ const pa_sample_spec *ss /**< The desired sample format */,
+ const pa_channel_map *map /**< The desired channel map, or NULL for default */,
+ pa_proplist *p /**< The initial property list */);
+
/** Decrease the reference counter by one */
void pa_stream_unref(pa_stream *s);
@@ -510,6 +522,17 @@ pa_operation *pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr
* is at least PulseAudio 0.9.8. \since 0.9.8 */
pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_stream_success_cb_t cb, void *userdata);
+/* Update the property list of the sink input/source output of this
+ * stream, adding new entries. Please note that it is highly
+ * recommended to set as much properties initially via
+ * pa_stream_new_with_proplist() as possible instead a posteriori with
+ * this function, since that information may then be used to route
+ * this stream to the right device. \since 0.9.10 */
+pa_operation *pa_stream_proplist_update(pa_stream *s, pa_update_mode_t mode, pa_proplist *p, pa_stream_success_cb_t cb, void *userdata);
+
+/* Update the property list of the sink input/source output of this stream, remove entries. \since 0.9.10 */
+pa_operation *pa_stream_proplist_remove(pa_stream *s, const char *const keys[], pa_stream_success_cb_t cb, void *userdata);
+
PA_C_DECL_END
#endif
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 22e5b8a4..004e88c1 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -101,10 +101,10 @@ PA_C_DECL_BEGIN
typedef uint32_t pa_volume_t;
/** Normal volume (100%) */
-#define PA_VOLUME_NORM (0x10000)
+#define PA_VOLUME_NORM ((pa_volume_t) 0x10000)
/** Muted volume (0%) */
-#define PA_VOLUME_MUTED (0)
+#define PA_VOLUME_MUTED ((pa_volume_t) 0)
/** A structure encapsulating a per-channel volume */
typedef struct pa_cvolume {