-- cgit From 347cfc356aa1c5073a5fc1d4355392759df13ab8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Mar 2008 15:18:55 +0000 Subject: commit glitch-free work git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2121 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 208 ++++++++++++++++++++++++++++++++++++++++++++++--- src/pulse/context.h | 24 +++++- src/pulse/def.h | 10 ++- src/pulse/internal.h | 7 +- src/pulse/introspect.c | 46 +++++++++-- src/pulse/introspect.h | 14 +++- src/pulse/proplist.c | 66 +++++++++++++--- src/pulse/proplist.h | 165 ++++++++++++++++++++++++++++++--------- src/pulse/sample.c | 10 ++- src/pulse/scache.c | 122 ++++++++++++++++++++++++++++- src/pulse/scache.h | 31 ++++++-- src/pulse/stream.c | 132 +++++++++++++++++++++++++++---- src/pulse/stream.h | 25 +++++- src/pulse/volume.h | 4 +- 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 #include #include +#include #ifdef HAVE_SYS_WAIT_H #include @@ -52,6 +53,8 @@ #include #include +#include +#include #include #include @@ -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 #include #include +#include /** \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 #include #include +#include /** \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 #include +#include #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 { -- cgit From 12c01e942d23bd477e14b467e66352e6ce0557a9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Mar 2008 15:19:40 +0000 Subject: commit glitch-free work git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2122 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/asyncmsgq.h | 1 + src/pulsecore/cli-command.c | 5 +- src/pulsecore/cli-text.c | 80 +++-- src/pulsecore/cli.c | 2 +- src/pulsecore/client.c | 21 +- src/pulsecore/client.h | 8 +- src/pulsecore/core-scache.c | 58 ++-- src/pulsecore/core-scache.h | 17 +- src/pulsecore/core.h | 16 +- src/pulsecore/envelope.c | 2 +- src/pulsecore/envelope.h | 2 +- src/pulsecore/fdsem.c | 96 ++++-- src/pulsecore/fdsem.h | 8 + src/pulsecore/macro.h | 46 ++- src/pulsecore/mcalign.c | 9 +- src/pulsecore/mcalign.h | 3 + src/pulsecore/memblock.c | 39 ++- src/pulsecore/memblock.h | 13 +- src/pulsecore/memblockq.c | 326 +++++++++++++++------ src/pulsecore/memblockq.h | 32 +- src/pulsecore/namereg.c | 2 +- src/pulsecore/namereg.h | 2 +- src/pulsecore/native-common.h | 10 +- src/pulsecore/play-memblockq.c | 106 ++++--- src/pulsecore/play-memblockq.h | 9 +- src/pulsecore/play-memchunk.c | 154 +--------- src/pulsecore/play-memchunk.h | 5 +- src/pulsecore/protocol-esound.c | 75 +++-- src/pulsecore/protocol-native.c | 599 +++++++++++++++++++++++++++++--------- src/pulsecore/protocol-simple.c | 78 ++--- src/pulsecore/pstream.c | 2 +- src/pulsecore/resampler.c | 153 +++++++++- src/pulsecore/resampler.h | 4 + src/pulsecore/rtpoll.c | 2 +- src/pulsecore/sample-util.c | 21 +- src/pulsecore/shmasyncq.c | 222 ++++++++++++++ src/pulsecore/shmasyncq.h | 62 ++++ src/pulsecore/sink-input.c | 500 +++++++++++++++++-------------- src/pulsecore/sink-input.h | 86 ++++-- src/pulsecore/sink.c | 374 ++++++++++++++++++------ src/pulsecore/sink.h | 84 ++++-- src/pulsecore/sound-file-stream.c | 179 +++++++----- src/pulsecore/source-output.c | 63 ++-- src/pulsecore/source-output.h | 29 +- src/pulsecore/source.c | 217 +++++++++++--- src/pulsecore/source.h | 54 +++- src/pulsecore/tagstruct.c | 87 +++++- src/pulsecore/tagstruct.h | 6 +- src/pulsecore/time-smoother.c | 34 +++ src/pulsecore/time-smoother.h | 10 +- 50 files changed, 2848 insertions(+), 1165 deletions(-) create mode 100644 src/pulsecore/shmasyncq.c create mode 100644 src/pulsecore/shmasyncq.h diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h index 5d3867ba..575f760f 100644 --- a/src/pulsecore/asyncmsgq.h +++ b/src/pulsecore/asyncmsgq.h @@ -56,6 +56,7 @@ typedef struct pa_asyncmsgq pa_asyncmsgq; pa_asyncmsgq* pa_asyncmsgq_new(unsigned size); pa_asyncmsgq* pa_asyncmsgq_ref(pa_asyncmsgq *q); + void pa_asyncmsgq_unref(pa_asyncmsgq* q); void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb); diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 3110a271..73b6619c 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -779,6 +779,7 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { const char *n, *sink_name; pa_sink *sink; + uint32_t idx; pa_core_assert_ref(c); pa_assert(t); @@ -795,11 +796,13 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu return -1; } - if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM) < 0) { + if (pa_scache_play_item(c, n, sink, PA_VOLUME_NORM, NULL, &idx) < 0) { pa_strbuf_puts(buf, "Failed to play sample.\n"); return -1; } + pa_strbuf_printf(buf, "Playing on sink input #%i\n", idx); + return 0; } diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index b64cafe2..8bb567b7 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "cli-text.h" @@ -78,10 +79,20 @@ char *pa_client_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients)); for (client = pa_idxset_first(c->clients, &idx); client; client = pa_idxset_next(c->clients, &idx)) { - pa_strbuf_printf(s, " index: %u\n\tname: <%s>\n\tdriver: <%s>\n", client->index, client->name, client->driver); + char *t; + pa_strbuf_printf( + s, + " index: %u\n" + "\tdriver: <%s>\n", + client->index, + client->driver); - if (client->owner) - pa_strbuf_printf(s, "\towner module: <%u>\n", client->owner->index); + if (client->module) + pa_strbuf_printf(s, "\towner module: <%u>\n", client->module->index); + + t = pa_proplist_to_string(client->proplist); + pa_strbuf_printf(s, "\tproperties:\n%s", t); + pa_xfree(t); } return pa_strbuf_tostring_free(s); @@ -92,6 +103,7 @@ char *pa_sink_list_to_string(pa_core *c) { pa_sink *sink; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { + [PA_SINK_INIT] = "INIT", [PA_SINK_RUNNING] = "RUNNING", [PA_SINK_SUSPENDED] = "SUSPENDED", [PA_SINK_IDLE] = "IDLE", @@ -104,14 +116,14 @@ char *pa_sink_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t; pa_strbuf_printf( s, " %c index: %u\n" "\tname: <%s>\n" "\tdriver: <%s>\n" - "\tflags: %s%s%s%s\n" + "\tflags: %s%s%s%s%s%s\n" "\tstate: %s\n" "\tvolume: <%s>\n" "\tmute: <%i>\n" @@ -125,7 +137,9 @@ char *pa_sink_list_to_string(pa_core *c) { sink->index, sink->name, sink->driver, + sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "", sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", + sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", sink->flags & PA_SINK_LATENCY ? "LATENCY " : "", sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "", sink->flags & PA_SINK_NETWORK ? "NETWORK " : "", @@ -141,8 +155,10 @@ char *pa_sink_list_to_string(pa_core *c) { if (sink->module) pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index); - if (sink->description) - pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description); + + t = pa_proplist_to_string(sink->proplist); + pa_strbuf_printf(s, "\tproperties:\n%s", t); + pa_xfree(t); } return pa_strbuf_tostring_free(s); @@ -153,6 +169,7 @@ char *pa_source_list_to_string(pa_core *c) { pa_source *source; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { + [PA_SOURCE_INIT] = "INIT", [PA_SOURCE_RUNNING] = "RUNNING", [PA_SOURCE_SUSPENDED] = "SUSPENDED", [PA_SOURCE_IDLE] = "IDLE", @@ -165,15 +182,14 @@ char *pa_source_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX]; - + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], *t; pa_strbuf_printf( s, " %c index: %u\n" "\tname: <%s>\n" "\tdriver: <%s>\n" - "\tflags: %s%s%s%s\n" + "\tflags: %s%s%s%s%s%s\n" "\tstate: %s\n" "\tvolume: <%s>\n" "\tmute: <%u>\n" @@ -186,7 +202,9 @@ char *pa_source_list_to_string(pa_core *c) { source->index, source->name, source->driver, + source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "", source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", + source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "", source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "", @@ -203,8 +221,10 @@ char *pa_source_list_to_string(pa_core *c) { pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); if (source->module) pa_strbuf_printf(s, "\tmodule: <%u>\n", source->module->index); - if (source->description) - pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description); + + t = pa_proplist_to_string(source->proplist); + pa_strbuf_printf(s, "\tproperties:\n%s", t); + pa_xfree(t); } return pa_strbuf_tostring_free(s); @@ -216,6 +236,7 @@ char *pa_source_output_list_to_string(pa_core *c) { pa_source_output *o; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { + [PA_SOURCE_OUTPUT_INIT] = "INIT", [PA_SOURCE_OUTPUT_RUNNING] = "RUNNING", [PA_SOURCE_OUTPUT_CORKED] = "CORKED", [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED" @@ -227,16 +248,15 @@ char *pa_source_output_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs)); for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t; pa_assert(o->source); pa_strbuf_printf( s, " index: %u\n" - "\tname: '%s'\n" "\tdriver: <%s>\n" - "\tflags: %s%s%s%s%s%s%s\n" + "\tflags: %s%s%s%s%s%s%s%s\n" "\tstate: %s\n" "\tsource: <%u> '%s'\n" "\tlatency: <%0.0f usec>\n" @@ -244,10 +264,10 @@ char *pa_source_output_list_to_string(pa_core *c) { "\tchannel map: <%s>\n" "\tresample method: %s\n", o->index, - o->name, o->driver, o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "", o->flags & PA_SOURCE_OUTPUT_DONT_MOVE ? "DONT_MOVE " : "", + o->flags & PA_SOURCE_OUTPUT_START_CORKED ? "START_CORKED " : "", o->flags & PA_SOURCE_OUTPUT_NO_REMAP ? "NO_REMAP " : "", o->flags & PA_SOURCE_OUTPUT_NO_REMIX ? "NO_REMIX " : "", o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "", @@ -262,7 +282,11 @@ char *pa_source_output_list_to_string(pa_core *c) { if (o->module) pa_strbuf_printf(s, "\towner module: <%u>\n", o->module->index); if (o->client) - pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, o->client->name); + pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME))); + + t = pa_proplist_to_string(o->proplist); + pa_strbuf_printf(s, "\tproperties:\n%s", t); + pa_xfree(t); } return pa_strbuf_tostring_free(s); @@ -273,6 +297,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_sink_input *i; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { + [PA_SINK_INPUT_INIT] = "INIT", [PA_SINK_INPUT_RUNNING] = "RUNNING", [PA_SINK_INPUT_DRAINED] = "DRAINED", [PA_SINK_INPUT_CORKED] = "CORKED", @@ -285,16 +310,15 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs)); for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t; pa_assert(i->sink); pa_strbuf_printf( s, " index: %u\n" - "\tname: <%s>\n" "\tdriver: <%s>\n" - "\tflags: %s%s%s%s%s%s%s\n" + "\tflags: %s%s%s%s%s%s%s%s\n" "\tstate: %s\n" "\tsink: <%u> '%s'\n" "\tvolume: <%s>\n" @@ -304,10 +328,10 @@ char *pa_sink_input_list_to_string(pa_core *c) { "\tchannel map: <%s>\n" "\tresample method: %s\n", i->index, - i->name, i->driver, i->flags & PA_SINK_INPUT_VARIABLE_RATE ? "VARIABLE_RATE " : "", i->flags & PA_SINK_INPUT_DONT_MOVE ? "DONT_MOVE " : "", + i->flags & PA_SINK_INPUT_START_CORKED ? "START_CORKED " : "", i->flags & PA_SINK_INPUT_NO_REMAP ? "NO_REMAP " : "", i->flags & PA_SINK_INPUT_NO_REMIX ? "NO_REMIX " : "", i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "", @@ -325,7 +349,11 @@ char *pa_sink_input_list_to_string(pa_core *c) { if (i->module) pa_strbuf_printf(s, "\tmodule: <%u>\n", i->module->index); if (i->client) - pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, i->client->name); + pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME))); + + t = pa_proplist_to_string(i->proplist); + pa_strbuf_printf(s, "\tproperties:\n%s", t); + pa_xfree(t); } return pa_strbuf_tostring_free(s); @@ -345,7 +373,7 @@ char *pa_scache_list_to_string(pa_core *c) { for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { double l = 0; - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a"; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t; if (e->memchunk.memblock) { pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec); @@ -371,8 +399,12 @@ char *pa_scache_list_to_string(pa_core *c) { (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0), l, pa_cvolume_snprint(cv, sizeof(cv), &e->volume), - e->lazy ? "yes" : "no", + pa_yes_no(e->lazy), e->filename ? e->filename : "n/a"); + + t = pa_proplist_to_string(e->proplist); + pa_strbuf_printf(s, "\tproperties:\n%s", t); + pa_xfree(t); } } diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c index 85e08634..47712d30 100644 --- a/src/pulsecore/cli.c +++ b/src/pulsecore/cli.c @@ -82,7 +82,7 @@ pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) { pa_assert_se(c->client = pa_client_new(core, __FILE__, cname)); c->client->kill = client_kill; c->client->userdata = c; - c->client->owner = m; + c->client->module = m; pa_ioline_set_callback(c->line, line_callback, c); pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT); diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index 319b8387..6a087d46 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -44,17 +44,19 @@ pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) { pa_core_assert_ref(core); c = pa_xnew(pa_client, 1); - c->name = pa_xstrdup(name); - c->driver = pa_xstrdup(driver); - c->owner = NULL; c->core = core; + c->proplist = pa_proplist_new(); + if (name) + pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name); + c->driver = pa_xstrdup(driver); + c->module = NULL; c->kill = NULL; c->userdata = NULL; pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0); - pa_log_info("Created %u \"%s\"", c->index, c->name); + pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name)); pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index); pa_core_check_quit(core); @@ -70,9 +72,9 @@ void pa_client_free(pa_client *c) { pa_core_check_quit(c->core); - pa_log_info("Freed %u \"%s\"", c->index, c->name); + pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME))); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); - pa_xfree(c->name); + pa_proplist_free(c->proplist); pa_xfree(c->driver); pa_xfree(c); } @@ -91,10 +93,7 @@ void pa_client_kill(pa_client *c) { void pa_client_set_name(pa_client *c, const char *name) { pa_assert(c); - pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, c->name, name); - - pa_xfree(c->name); - c->name = pa_xstrdup(name); - + pa_log_info("Client %u changed name from \"%s\" to \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)), name); + pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); } diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h index 6d09b999..bff057ed 100644 --- a/src/pulsecore/client.h +++ b/src/pulsecore/client.h @@ -28,6 +28,7 @@ typedef struct pa_client pa_client; +#include #include #include @@ -37,11 +38,12 @@ typedef struct pa_client pa_client; struct pa_client { uint32_t index; - - pa_module *owner; - char *name, *driver; pa_core *core; + pa_proplist *proplist; + pa_module *module; + char *driver; + void (*kill)(pa_client *c); void *userdata; }; diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index 46444a90..4036a55b 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -63,7 +63,7 @@ #include "core-scache.h" -#define UNLOAD_POLL_TIME 2 +#define UNLOAD_POLL_TIME 5 static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { pa_core *c = userdata; @@ -89,6 +89,8 @@ static void free_entry(pa_scache_entry *e) { pa_xfree(e->filename); if (e->memchunk.memblock) pa_memblock_unref(e->memchunk.memblock); + if (e->proplist) + pa_proplist_free(e->proplist); pa_xfree(e); } @@ -103,6 +105,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { pa_memblock_unref(e->memchunk.memblock); pa_xfree(e->filename); + pa_proplist_clear(e->proplist); pa_assert(e->core == c); @@ -117,11 +120,10 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { e->name = pa_xstrdup(name); e->core = c; + e->proplist = pa_proplist_new(); - if (!c->scache) { + if (!c->scache) c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - pa_assert(c->scache); - } pa_idxset_put(c->scache, e, &e->index); @@ -132,7 +134,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { e->memchunk.memblock = NULL; e->memchunk.index = e->memchunk.length = 0; e->filename = NULL; - e->lazy = 0; + e->lazy = FALSE; e->last_used_time = 0; memset(&e->sample_spec, 0, sizeof(e->sample_spec)); @@ -142,7 +144,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { return e; } -int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx) { +int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx) { pa_scache_entry *e; char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; pa_channel_map tmap; @@ -178,6 +180,9 @@ int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, c pa_memblock_ref(e->memchunk.memblock); } + if (p) + pa_proplist_update(e->proplist, PA_UPDATE_REPLACE, p); + if (idx) *idx = e->index; @@ -208,7 +213,7 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3 if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0) return -1; - r = pa_scache_add_item(c, name, &ss, &map, &chunk, idx); + r = pa_scache_add_item(c, name, &ss, &map, &chunk, NULL, idx); pa_memblock_unref(chunk.memblock); return r; @@ -231,7 +236,7 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, if (!(e = scache_add_item(c, name))) return -1; - e->lazy = 1; + e->lazy = TRUE; e->filename = pa_xstrdup(filename); if (!c->scache_auto_unload_event) { @@ -285,10 +290,11 @@ void pa_scache_free(pa_core *c) { c->mainloop->time_free(c->scache_auto_unload_event); } -int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume) { +int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) { pa_scache_entry *e; char *t; pa_cvolume r; + pa_proplist *merged; pa_assert(c); pa_assert(name); @@ -312,17 +318,24 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name); - t = pa_sprintf_malloc("sample:%s", name); - pa_cvolume_set(&r, e->volume.channels, volume); pa_sw_cvolume_multiply(&r, &r, &e->volume); - if (pa_play_memchunk(sink, t, &e->sample_spec, &e->channel_map, &e->memchunk, &r) < 0) { - pa_xfree(t); + merged = pa_proplist_new(); + + t = pa_sprintf_malloc("sample:%s", name); + pa_proplist_sets(merged, PA_PROP_MEDIA_NAME, t); + pa_xfree(t); + + pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist); + pa_proplist_update(merged, PA_UPDATE_REPLACE, p); + + if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) { + pa_proplist_free(merged); return -1; } - pa_xfree(t); + pa_proplist_free(merged); if (e->lazy) time(&e->last_used_time); @@ -330,7 +343,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t return 0; } -int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload) { +int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) { pa_sink *sink; pa_assert(c); @@ -339,10 +352,10 @@ int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_na if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload))) return -1; - return pa_scache_play_item(c, name, sink, volume); + return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx); } -const char * pa_scache_get_name_by_id(pa_core *c, uint32_t id) { +const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id) { pa_scache_entry *e; pa_assert(c); @@ -366,9 +379,10 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) { return e->index; } -uint32_t pa_scache_total_size(pa_core *c) { +size_t pa_scache_total_size(pa_core *c) { pa_scache_entry *e; - uint32_t idx, sum = 0; + uint32_t idx; + size_t sum = 0; pa_assert(c); @@ -403,8 +417,7 @@ void pa_scache_unload_unused(pa_core *c) { continue; pa_memblock_unref(e->memchunk.memblock); - e->memchunk.memblock = NULL; - e->memchunk.index = e->memchunk.length = 0; + pa_memchunk_reset(&e->memchunk); pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); } @@ -467,8 +480,9 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) { pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name); add_file(c, p); } + + closedir(dir); } - closedir(dir); return 0; } diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h index ab7ec0ef..31f3ff32 100644 --- a/src/pulsecore/core-scache.h +++ b/src/pulsecore/core-scache.h @@ -29,11 +29,12 @@ #include #include -#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*2) +#define PA_SCACHE_ENTRY_SIZE_MAX (1024*1024*16) typedef struct pa_scache_entry { - pa_core *core; uint32_t index; + pa_core *core; + char *name; pa_cvolume volume; @@ -43,25 +44,27 @@ typedef struct pa_scache_entry { char *filename; - int lazy; + pa_bool_t lazy; time_t last_used_time; + + pa_proplist *proplist; } pa_scache_entry; -int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, uint32_t *idx); +int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx); int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint32_t *idx); int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, uint32_t *idx); int pa_scache_add_directory_lazy(pa_core *c, const char *pathname); int pa_scache_remove_item(pa_core *c, const char *name); -int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume); -int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, int autoload); +int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx); +int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx); void pa_scache_free(pa_core *c); const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id); uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name); -uint32_t pa_scache_total_size(pa_core *c); +size_t pa_scache_total_size(pa_core *c); void pa_scache_unload_unused(pa_core *c); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index ce45e300..6be1a0c5 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -43,16 +43,20 @@ typedef struct pa_core pa_core; #include typedef enum pa_core_hook { - PA_CORE_HOOK_SINK_NEW_POST, + PA_CORE_HOOK_SINK_NEW, + PA_CORE_HOOK_SINK_FIXATE, + PA_CORE_HOOK_SINK_PUT, PA_CORE_HOOK_SINK_UNLINK, PA_CORE_HOOK_SINK_UNLINK_POST, PA_CORE_HOOK_SINK_STATE_CHANGED, - PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED, - PA_CORE_HOOK_SOURCE_NEW_POST, + PA_CORE_HOOK_SINK_PROPLIST_CHANGED, + PA_CORE_HOOK_SOURCE_NEW, + PA_CORE_HOOK_SOURCE_FIXATE, + PA_CORE_HOOK_SOURCE_PUT, PA_CORE_HOOK_SOURCE_UNLINK, PA_CORE_HOOK_SOURCE_UNLINK_POST, PA_CORE_HOOK_SOURCE_STATE_CHANGED, - PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED, + PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED, PA_CORE_HOOK_SINK_INPUT_NEW, PA_CORE_HOOK_SINK_INPUT_FIXATE, PA_CORE_HOOK_SINK_INPUT_PUT, @@ -60,8 +64,8 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SINK_INPUT_UNLINK_POST, PA_CORE_HOOK_SINK_INPUT_MOVE, PA_CORE_HOOK_SINK_INPUT_MOVE_POST, - PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED, PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED, + PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, PA_CORE_HOOK_SOURCE_OUTPUT_NEW, PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE, PA_CORE_HOOK_SOURCE_OUTPUT_PUT, @@ -69,8 +73,8 @@ typedef enum pa_core_hook { PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST, PA_CORE_HOOK_SOURCE_OUTPUT_MOVE, PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST, - PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED, PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED, + PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED, PA_CORE_HOOK_MAX } pa_core_hook_t; diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c index 571f8754..2f5da5a0 100644 --- a/src/pulsecore/envelope.c +++ b/src/pulsecore/envelope.c @@ -381,7 +381,7 @@ static void envelope_merge(pa_envelope *e, int v) { break; if (e->points[v].n_points >= e->points[v].n_allocated) { - e->points[v].n_allocated = MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX); + e->points[v].n_allocated = PA_MAX(e->points[v].n_points*2, PA_ENVELOPE_POINTS_MAX); e->points[v].x = pa_xrealloc(e->points[v].x, sizeof(size_t) * e->points[v].n_allocated); e->points[v].y.i = pa_xrealloc(e->points[v].y.i, sizeof(int32_t) * e->points[v].n_allocated); diff --git a/src/pulsecore/envelope.h b/src/pulsecore/envelope.h index 23be8f6a..c54c137a 100644 --- a/src/pulsecore/envelope.h +++ b/src/pulsecore/envelope.h @@ -29,7 +29,7 @@ #include -#define PA_ENVELOPE_POINTS_MAX 4 +#define PA_ENVELOPE_POINTS_MAX 4U typedef struct pa_envelope pa_envelope; typedef struct pa_envelope_item pa_envelope_item; diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c index 59eec18e..22d2a850 100644 --- a/src/pulsecore/fdsem.c +++ b/src/pulsecore/fdsem.c @@ -78,21 +78,19 @@ struct pa_fdsem { #ifdef HAVE_EVENTFD int efd; #endif - pa_atomic_t waiting; - pa_atomic_t signalled; - pa_atomic_t in_pipe; + + pa_fdsem_data *data; }; pa_fdsem *pa_fdsem_new(void) { pa_fdsem *f; - f = pa_xnew(pa_fdsem, 1); + f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data))); #ifdef HAVE_EVENTFD if ((f->efd = eventfd(0)) >= 0) { pa_make_fd_cloexec(f->efd); f->fds[0] = f->fds[1] = -1; - } else #endif { @@ -105,9 +103,57 @@ pa_fdsem *pa_fdsem_new(void) { pa_make_fd_cloexec(f->fds[1]); } - pa_atomic_store(&f->waiting, 0); - pa_atomic_store(&f->signalled, 0); - pa_atomic_store(&f->in_pipe, 0); + f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem))); + + pa_atomic_store(&f->data->waiting, 0); + pa_atomic_store(&f->data->signalled, 0); + pa_atomic_store(&f->data->in_pipe, 0); + + return f; +} + +pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd) { + pa_fdsem *f = NULL; + + pa_assert(data); + pa_assert(event_fd >= 0); + +#ifdef HAVE_EVENTFD + f = pa_xnew(pa_fdsem, 1); + + f->efd = event_fd; + pa_make_fd_cloexec(f->efd); + f->fds[0] = f->fds[1] = -1; + f->data = data; +#endif + + return f; +} + +pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd) { + pa_fdsem *f = NULL; + + pa_assert(data); + pa_assert(event_fd); + +#ifdef HAVE_EVENTFD + + f = pa_xnew(pa_fdsem, 1); + + if ((f->efd = eventfd(0)) < 0) { + pa_xfree(f); + return NULL; + } + + pa_make_fd_cloexec(f->efd); + f->fds[0] = f->fds[1] = -1; + f->data = data; + + pa_atomic_store(&f->data->waiting, 0); + pa_atomic_store(&f->data->signalled, 0); + pa_atomic_store(&f->data->in_pipe, 0); + +#endif return f; } @@ -128,7 +174,7 @@ static void flush(pa_fdsem *f) { ssize_t r; pa_assert(f); - if (pa_atomic_load(&f->in_pipe) <= 0) + if (pa_atomic_load(&f->data->in_pipe) <= 0) return; do { @@ -151,19 +197,19 @@ static void flush(pa_fdsem *f) { continue; } - } while (pa_atomic_sub(&f->in_pipe, r) > r); + } while (pa_atomic_sub(&f->data->in_pipe, r) > r); } void pa_fdsem_post(pa_fdsem *f) { pa_assert(f); - if (pa_atomic_cmpxchg(&f->signalled, 0, 1)) { + if (pa_atomic_cmpxchg(&f->data->signalled, 0, 1)) { - if (pa_atomic_load(&f->waiting)) { + if (pa_atomic_load(&f->data->waiting)) { ssize_t r; char x = 'x'; - pa_atomic_inc(&f->in_pipe); + pa_atomic_inc(&f->data->in_pipe); for (;;) { @@ -194,12 +240,12 @@ void pa_fdsem_wait(pa_fdsem *f) { flush(f); - if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) + if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) return; - pa_atomic_inc(&f->waiting); + pa_atomic_inc(&f->data->waiting); - while (!pa_atomic_cmpxchg(&f->signalled, 1, 0)) { + while (!pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) { char x[10]; ssize_t r; @@ -221,10 +267,10 @@ void pa_fdsem_wait(pa_fdsem *f) { continue; } - pa_atomic_sub(&f->in_pipe, r); + pa_atomic_sub(&f->data->in_pipe, r); } - pa_assert_se(pa_atomic_dec(&f->waiting) >= 1); + pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1); } int pa_fdsem_try(pa_fdsem *f) { @@ -232,7 +278,7 @@ int pa_fdsem_try(pa_fdsem *f) { flush(f); - if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) + if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) return 1; return 0; @@ -254,13 +300,13 @@ int pa_fdsem_before_poll(pa_fdsem *f) { flush(f); - if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) + if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) return -1; - pa_atomic_inc(&f->waiting); + pa_atomic_inc(&f->data->waiting); - if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) { - pa_assert_se(pa_atomic_dec(&f->waiting) >= 1); + if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) { + pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1); return -1; } return 0; @@ -269,11 +315,11 @@ int pa_fdsem_before_poll(pa_fdsem *f) { int pa_fdsem_after_poll(pa_fdsem *f) { pa_assert(f); - pa_assert_se(pa_atomic_dec(&f->waiting) >= 1); + pa_assert_se(pa_atomic_dec(&f->data->waiting) >= 1); flush(f); - if (pa_atomic_cmpxchg(&f->signalled, 1, 0)) + if (pa_atomic_cmpxchg(&f->data->signalled, 1, 0)) return 1; return 0; diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h index f38ef205..f4f7b99a 100644 --- a/src/pulsecore/fdsem.h +++ b/src/pulsecore/fdsem.h @@ -33,7 +33,15 @@ typedef struct pa_fdsem pa_fdsem; +typedef struct pa_fdsem_data { + pa_atomic_t waiting; + pa_atomic_t signalled; + pa_atomic_t in_pipe; +} pa_fdsem_data; + pa_fdsem *pa_fdsem_new(void); +pa_fdsem *pa_fdsem_open_shm(pa_fdsem_data *data, int event_fd); +pa_fdsem *pa_fdsem_new_shm(pa_fdsem_data *data, int* event_fd); void pa_fdsem_free(pa_fdsem *f); void pa_fdsem_post(pa_fdsem *f); diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index 41af19c9..60ab025c 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -65,19 +65,53 @@ static inline size_t pa_page_align(size_t l) { #define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) +/* The users of PA_MIN and PA_MAX should be aware that these macros on + * non-GCC executed code with side effects twice. It is thus + * considered misuse to use code with side effects as arguments to MIN + * and MAX. */ + +#ifdef __GNUC__ +#define PA_MAX(a,b) \ + __extension__ ({ typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#else +#define PA_MAX(a, b) ((a) > (b) ? (a) : (b)) #endif -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#ifdef __GNUC__ +#define PA_MIN(a,b) \ + __extension__ ({ typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#else +#define PA_MIN(a, b) ((a) < (b) ? (a) : (b)) #endif -#ifndef CLAMP -#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#ifdef __GNUC__ +#define PA_CLAMP(x, low, high) \ + __extension__ ({ typeof(x) _x = (x); \ + typeof(low) _low = (low); \ + typeof(high) _high = (high); \ + ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \ + }) +#else +#define PA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) #endif +#ifdef __GNUC__ +#define PA_CLAMP_UNLIKELY(x, low, high) \ + __extension__ ({ typeof(x) _x = (x); \ + typeof(low) _low = (low); \ + typeof(high) _high = (high); \ + (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \ + }) +#else #define PA_CLAMP_UNLIKELY(x, low, high) (PA_UNLIKELY((x) > (high)) ? (high) : (PA_UNLIKELY((x) < (low)) ? (low) : (x))) +#endif + /* We don't define a PA_CLAMP_LIKELY here, because it doesn't really * make sense: we cannot know if it is more likely that the values is * lower or greater than the boundaries.*/ diff --git a/src/pulsecore/mcalign.c b/src/pulsecore/mcalign.c index 8ca7c962..e12f84f8 100644 --- a/src/pulsecore/mcalign.c +++ b/src/pulsecore/mcalign.c @@ -197,7 +197,6 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c) { /* There's simply nothing */ return -1; - } size_t pa_mcalign_csize(pa_mcalign *m, size_t l) { @@ -211,3 +210,11 @@ size_t pa_mcalign_csize(pa_mcalign *m, size_t l) { return (l/m->base)*m->base; } + +void pa_mcalign_flush(pa_mcalign *m) { + pa_memchunk chunk; + pa_assert(m); + + while (pa_mcalign_pop(m, &chunk) >= 0) + pa_memblock_unref(chunk.memblock); +} diff --git a/src/pulsecore/mcalign.h b/src/pulsecore/mcalign.h index 6ff8f94e..6c8b8d5f 100644 --- a/src/pulsecore/mcalign.h +++ b/src/pulsecore/mcalign.h @@ -79,4 +79,7 @@ int pa_mcalign_pop(pa_mcalign *m, pa_memchunk *c); /* If we pass l bytes in now, how many bytes would we get out? */ size_t pa_mcalign_csize(pa_mcalign *m, size_t l); +/* Flush what's still stored in the aligner */ +void pa_mcalign_flush(pa_mcalign *m); + #endif diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index 99b5a13f..b76980ff 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -59,7 +59,7 @@ struct pa_memblock { pa_mempool *pool; pa_memblock_type_t type; - int read_only; /* boolean */ + pa_bool_t read_only, is_silence; pa_atomic_ptr_t data; size_t length; @@ -226,7 +226,8 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) { PA_REFCNT_INIT(b); b->pool = p; b->type = PA_MEMBLOCK_APPENDED; - b->read_only = 0; + b->read_only = FALSE; + b->is_silence = FALSE; pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); b->length = length; pa_atomic_store(&b->n_acquired, 0); @@ -330,7 +331,8 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { PA_REFCNT_INIT(b); b->pool = p; - b->read_only = 0; + b->read_only = FALSE; + b->is_silence = FALSE; b->length = length; pa_atomic_store(&b->n_acquired, 0); pa_atomic_store(&b->please_signal, 0); @@ -340,7 +342,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { } /* No lock necessary */ -pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) { +pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) { pa_memblock *b; pa_assert(p); @@ -354,6 +356,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re b->pool = p; b->type = PA_MEMBLOCK_FIXED; b->read_only = read_only; + b->is_silence = FALSE; pa_atomic_ptr_store(&b->data, d); b->length = length; pa_atomic_store(&b->n_acquired, 0); @@ -364,7 +367,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re } /* No lock necessary */ -pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) { +pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only) { pa_memblock *b; pa_assert(p); @@ -379,6 +382,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (* b->pool = p; b->type = PA_MEMBLOCK_USER; b->read_only = read_only; + b->is_silence = FALSE; pa_atomic_ptr_store(&b->data, d); b->length = length; pa_atomic_store(&b->n_acquired, 0); @@ -391,7 +395,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (* } /* No lock necessary */ -int pa_memblock_is_read_only(pa_memblock *b) { +pa_bool_t pa_memblock_is_read_only(pa_memblock *b) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) > 0); @@ -399,13 +403,28 @@ int pa_memblock_is_read_only(pa_memblock *b) { } /* No lock necessary */ -int pa_memblock_ref_is_one(pa_memblock *b) { +pa_bool_t pa_memblock_is_silence(pa_memblock *b) { + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) > 0); + + return b->is_silence; +} + +/* No lock necessary */ +void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) { + pa_assert(b); + pa_assert(PA_REFCNT_VALUE(b) > 0); + + b->is_silence = v; +} + +/* No lock necessary */ +pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) { int r; pa_assert(b); - r = PA_REFCNT_VALUE(b); - pa_assert(r > 0); + pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0); return r == 1; } @@ -767,7 +786,7 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) { } /* No lock necessary */ -int pa_mempool_is_shared(pa_mempool *p) { +pa_bool_t pa_mempool_is_shared(pa_mempool *p) { pa_assert(p); return !!p->memory.shared; diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h index c704014a..338d1a13 100644 --- a/src/pulsecore/memblock.h +++ b/src/pulsecore/memblock.h @@ -87,13 +87,13 @@ pa_memblock *pa_memblock_new(pa_mempool *, size_t length); pa_memblock *pa_memblock_new_pool(pa_mempool *, size_t length); /* Allocate a new memory block of type PA_MEMBLOCK_USER */ -pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, void (*free_cb)(void *p), int read_only); +pa_memblock *pa_memblock_new_user(pa_mempool *, void *data, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only); /* A special case of pa_memblock_new_user: take a memory buffer previously allocated with pa_xmalloc() */ #define pa_memblock_new_malloced(p,data,length) pa_memblock_new_user(p, data, length, pa_xfree, 0) /* Allocate a new memory block of type PA_MEMBLOCK_FIXED */ -pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, int read_only); +pa_memblock *pa_memblock_new_fixed(pa_mempool *, void *data, size_t length, pa_bool_t read_only); void pa_memblock_unref(pa_memblock*b); pa_memblock* pa_memblock_ref(pa_memblock*b); @@ -106,8 +106,11 @@ function is not multiple caller safe, i.e. needs to be locked manually if called from more than one thread at the same time. */ void pa_memblock_unref_fixed(pa_memblock*b); -int pa_memblock_is_read_only(pa_memblock *b); -int pa_memblock_ref_is_one(pa_memblock *b); +pa_bool_t pa_memblock_is_read_only(pa_memblock *b); +pa_bool_t pa_memblock_is_silence(pa_memblock *b); +pa_bool_t pa_memblock_ref_is_one(pa_memblock *b); +void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v); + void* pa_memblock_acquire(pa_memblock *b); void pa_memblock_release(pa_memblock *b); size_t pa_memblock_get_length(pa_memblock *b); @@ -121,7 +124,7 @@ void pa_mempool_free(pa_mempool *p); const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p); void pa_mempool_vacuum(pa_mempool *p); int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id); -int pa_mempool_is_shared(pa_mempool *p); +pa_bool_t pa_mempool_is_shared(pa_mempool *p); size_t pa_mempool_block_size_max(pa_mempool *p); /* For recieving blocks from other nodes */ diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index 8247feab..cc5e9e13 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -50,8 +50,9 @@ PA_STATIC_FLIST_DECLARE(list_items, 0, pa_xfree); struct pa_memblockq { struct list_item *blocks, *blocks_tail; + struct list_item *current_read, *current_write; unsigned n_blocks; - size_t maxlength, tlength, base, prebuf, minreq; + size_t maxlength, tlength, base, prebuf, minreq, maxrewind; int64_t read_index, write_index; pa_bool_t in_prebuf; pa_memblock *silence; @@ -67,6 +68,7 @@ pa_memblockq* pa_memblockq_new( size_t base, size_t prebuf, size_t minreq, + size_t maxrewind, pa_memblock *silence) { pa_memblockq* bq; @@ -75,27 +77,29 @@ pa_memblockq* pa_memblockq_new( bq = pa_xnew(pa_memblockq, 1); bq->blocks = bq->blocks_tail = NULL; + bq->current_read = bq->current_write = NULL; bq->n_blocks = 0; bq->base = base; bq->read_index = bq->write_index = idx; - pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu", - (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq); + pa_log_debug("memblockq requested: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu", + (unsigned long) maxlength, (unsigned long) tlength, (unsigned long) base, (unsigned long) prebuf, (unsigned long) minreq, (unsigned long) maxrewind); - bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = 0; + bq->missing = bq->requested = bq->maxlength = bq->tlength = bq->prebuf = bq->minreq = bq->maxrewind = 0; bq->in_prebuf = TRUE; pa_memblockq_set_maxlength(bq, maxlength); pa_memblockq_set_tlength(bq, tlength); pa_memblockq_set_prebuf(bq, prebuf); pa_memblockq_set_minreq(bq, minreq); + pa_memblockq_set_maxrewind(bq, maxrewind); - pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu", - (unsigned long)bq->maxlength, (unsigned long)bq->tlength, (unsigned long)bq->base, (unsigned long)bq->prebuf, (unsigned long)bq->minreq); + pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu", + (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind); bq->silence = silence ? pa_memblock_ref(silence) : NULL; - bq->mcalign = NULL; + bq->mcalign = pa_mcalign_new(bq->base); return bq; } @@ -114,6 +118,62 @@ void pa_memblockq_free(pa_memblockq* bq) { pa_xfree(bq); } +static void fix_current_read(pa_memblockq *bq) { + pa_assert(bq); + + if (PA_UNLIKELY(!bq->blocks)) { + bq->current_read = NULL; + return; + } + + if (PA_UNLIKELY(!bq->current_read)) + bq->current_read = bq->blocks; + + /* Scan left */ + while (PA_UNLIKELY(bq->current_read->index > bq->read_index)) + + if (bq->current_read->prev) + bq->current_read = bq->current_read->prev; + else + break; + + /* Scan right */ + while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + bq->current_read->chunk.length <= bq->read_index)) + bq->current_read = bq->current_read->next; + + /* At this point current_read will either point at or left of the + next block to play. It may be NULL in case everything in + the queue was already played */ +} + +static void fix_current_write(pa_memblockq *bq) { + pa_assert(bq); + + if (PA_UNLIKELY(!bq->blocks)) { + bq->current_write = NULL; + return; + } + + if (PA_UNLIKELY(!bq->current_write)) + bq->current_write = bq->blocks_tail; + + /* Scan right */ + while (PA_UNLIKELY(bq->current_write->index + bq->current_write->chunk.length <= bq->write_index)) + + if (bq->current_write->next) + bq->current_write = bq->current_write->next; + else + break; + + /* Scan left */ + while (PA_LIKELY(bq->current_write != NULL) && PA_UNLIKELY(bq->current_write->index > bq->write_index)) + bq->current_write = bq->current_write->prev; + + /* At this point current_write will either point at or right of + the next block to write data to. It may be NULL in case + everything in the queue is still to be played */ +} + static void drop_block(pa_memblockq *bq, struct list_item *q) { pa_assert(bq); pa_assert(q); @@ -122,13 +182,23 @@ static void drop_block(pa_memblockq *bq, struct list_item *q) { if (q->prev) q->prev->next = q->next; - else + else { + pa_assert(bq->blocks == q); bq->blocks = q->next; + } if (q->next) q->next->prev = q->prev; - else + else { + pa_assert(bq->blocks_tail == q); bq->blocks_tail = q->prev; + } + + if (bq->current_write == q) + bq->current_write = q->prev; + + if (bq->current_read == q) + bq->current_read = q->next; pa_memblock_unref(q->chunk.memblock); @@ -138,6 +208,16 @@ static void drop_block(pa_memblockq *bq, struct list_item *q) { bq->n_blocks--; } +static void drop_backlog(pa_memblockq *bq) { + int64_t boundary; + pa_assert(bq); + + boundary = bq->read_index - bq->maxrewind; + + while (bq->blocks && (bq->blocks->index + bq->blocks->chunk.length <= boundary)) + drop_block(bq, bq->blocks); +} + static pa_bool_t can_push(pa_memblockq *bq, size_t l) { int64_t end; @@ -152,10 +232,10 @@ static pa_bool_t can_push(pa_memblockq *bq, size_t l) { return TRUE; } - end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : 0; + end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : bq->write_index; /* Make sure that the list doesn't get too long */ - if (bq->write_index + (int64_t)l > end) + if (bq->write_index + l > end) if (bq->write_index + l - bq->read_index > bq->maxlength) return FALSE; @@ -182,34 +262,32 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) { old = bq->write_index; chunk = *uchunk; - if (bq->read_index > bq->write_index) { + fix_current_write(bq); + q = bq->current_write; - /* We currently have a buffer underflow, we need to drop some - * incoming data */ + /* First we advance the q pointer right of where we want to + * write to */ - size_t d = bq->read_index - bq->write_index; - - if (chunk.length > d) { - chunk.index += d; - chunk.length -= d; - bq->write_index += d; - } else { - /* We drop the incoming data completely */ - bq->write_index += chunk.length; - goto finish; - } + if (q) { + while (bq->write_index + chunk.length > q->index) + if (q->next) + q = q->next; + else + break; } + if (!q) + q = bq->blocks_tail; + /* We go from back to front to look for the right place to add * this new entry. Drop data we will overwrite on the way */ - q = bq->blocks_tail; while (q) { - if (bq->write_index >= q->index + (int64_t) q->chunk.length) + if (bq->write_index >= q->index + q->chunk.length) /* We found the entry where we need to place the new entry immediately after */ break; - else if (bq->write_index + (int64_t) chunk.length <= q->index) { + else if (bq->write_index + chunk.length <= q->index) { /* This entry isn't touched at all, let's skip it */ q = q->prev; } else if (bq->write_index <= q->index && @@ -364,6 +442,7 @@ static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) { } int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { + int64_t d; pa_assert(bq); pa_assert(chunk); @@ -371,27 +450,35 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { if (memblockq_check_prebuf(bq)) return -1; + fix_current_read(bq); + /* Do we need to spit out silence? */ - if (!bq->blocks || bq->blocks->index > bq->read_index) { + if (!bq->current_read || bq->current_read->index > bq->read_index) { size_t length; /* How much silence shall we return? */ - length = bq->blocks ? bq->blocks->index - bq->read_index : 0; + if (bq->current_read) + length = bq->current_read->index - bq->read_index; + else if (bq->write_index > bq->read_index) + length = (size_t) (bq->write_index - bq->read_index); + else + length = 0; /* We need to return silence, since no data is yet available */ if (bq->silence) { + size_t l; + chunk->memblock = pa_memblock_ref(bq->silence); - if (!length || length > pa_memblock_get_length(chunk->memblock)) - length = pa_memblock_get_length(chunk->memblock); + l = pa_memblock_get_length(chunk->memblock); + chunk->length = (length <= 0 || length > l) ? l : length; - chunk->length = length; } else { /* If the memblockq is empty, return -1, otherwise return * the time to sleep */ - if (!bq->blocks) + if (length <= 0) return -1; chunk->memblock = NULL; @@ -403,11 +490,14 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { } /* Ok, let's pass real data to the caller */ - pa_assert(bq->blocks->index == bq->read_index); - - *chunk = bq->blocks->chunk; + *chunk = bq->current_read->chunk; pa_memblock_ref(chunk->memblock); + pa_assert(bq->read_index >= bq->current_read->index); + d = bq->read_index - bq->current_read->index; + chunk->index += d; + chunk->length -= d; + return 0; } @@ -424,42 +514,23 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) { if (memblockq_check_prebuf(bq)) break; - if (bq->blocks) { - size_t d; - - pa_assert(bq->blocks->index >= bq->read_index); - - d = (size_t) (bq->blocks->index - bq->read_index); - - if (d >= length) { - /* The first block is too far in the future */ - - bq->read_index += length; - break; - } else { - - length -= d; - bq->read_index += d; - } + fix_current_read(bq); - pa_assert(bq->blocks->index == bq->read_index); + if (bq->current_read) { + int64_t p, d; - if (bq->blocks->chunk.length <= length) { - /* We need to drop the full block */ + /* We go through this piece by piece to make sure we don't + * drop more than allowed by prebuf */ - length -= bq->blocks->chunk.length; - bq->read_index += bq->blocks->chunk.length; + p = bq->current_read->index + bq->current_read->chunk.length; + pa_assert(p >= bq->read_index); + d = p - bq->read_index; - drop_block(bq, bq->blocks); - } else { - /* Only the start of this block needs to be dropped */ + if (d > length) + d = length; - bq->blocks->chunk.index += length; - bq->blocks->chunk.length -= length; - bq->blocks->index += length; - bq->read_index += length; - break; - } + bq->read_index += d; + length -= d; } else { @@ -469,20 +540,22 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) { } } + drop_backlog(bq); + delta = bq->read_index - old; bq->missing += delta; } -int pa_memblockq_is_readable(pa_memblockq *bq) { +pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq) { pa_assert(bq); if (memblockq_check_prebuf(bq)) - return 0; + return FALSE; if (pa_memblockq_get_length(bq) <= 0) - return 0; + return FALSE; - return 1; + return TRUE; } size_t pa_memblockq_get_length(pa_memblockq *bq) { @@ -506,12 +579,6 @@ size_t pa_memblockq_missing(pa_memblockq *bq) { return l >= bq->minreq ? l : 0; } -size_t pa_memblockq_get_minreq(pa_memblockq *bq) { - pa_assert(bq); - - return bq->minreq; -} - void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) { int64_t old, delta; pa_assert(bq); @@ -535,6 +602,8 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) { pa_assert_not_reached(); } + drop_backlog(bq); + delta = bq->write_index - old; if (delta >= bq->requested) { @@ -564,7 +633,7 @@ void pa_memblockq_flush(pa_memblockq *bq) { delta = bq->write_index - old; - if (delta > bq->requested) { + if (delta >= bq->requested) { delta -= bq->requested; bq->requested = 0; } else if (delta >= 0) { @@ -581,13 +650,21 @@ size_t pa_memblockq_get_tlength(pa_memblockq *bq) { return bq->tlength; } +size_t pa_memblockq_get_minreq(pa_memblockq *bq) { + pa_assert(bq); + + return bq->minreq; +} + int64_t pa_memblockq_get_read_index(pa_memblockq *bq) { pa_assert(bq); + return bq->read_index; } int64_t pa_memblockq_get_write_index(pa_memblockq *bq) { pa_assert(bq); + return bq->write_index; } @@ -600,9 +677,6 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) { if (bq->base == 1) return pa_memblockq_push(bq, chunk); - if (!bq->mcalign) - bq->mcalign = pa_mcalign_new(bq->base); - if (!can_push(bq, pa_mcalign_csize(bq->mcalign, chunk->length))) return -1; @@ -613,23 +687,15 @@ int pa_memblockq_push_align(pa_memblockq* bq, const pa_memchunk *chunk) { r = pa_memblockq_push(bq, &rchunk); pa_memblock_unref(rchunk.memblock); - if (r < 0) + if (r < 0) { + pa_mcalign_flush(bq->mcalign); return -1; + } } return 0; } -void pa_memblockq_shorten(pa_memblockq *bq, size_t length) { - size_t l; - pa_assert(bq); - - l = pa_memblockq_get_length(bq); - - if (l > length) - pa_memblockq_drop(bq, l - length); -} - void pa_memblockq_prebuf_disable(pa_memblockq *bq) { pa_assert(bq); @@ -639,7 +705,7 @@ void pa_memblockq_prebuf_disable(pa_memblockq *bq) { void pa_memblockq_prebuf_force(pa_memblockq *bq) { pa_assert(bq); - if (!bq->in_prebuf && bq->prebuf > 0) + if (bq->prebuf > 0) bq->in_prebuf = TRUE; } @@ -710,7 +776,7 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) { void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) { pa_assert(bq); - bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength/2 : prebuf; + bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength : prebuf; bq->prebuf = ((bq->prebuf+bq->base-1)/bq->base)*bq->base; if (prebuf > 0 && bq->prebuf < bq->base) @@ -737,3 +803,73 @@ void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) { if (bq->minreq < bq->base) bq->minreq = bq->base; } + +void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) { + pa_assert(bq); + + bq->maxrewind = (maxrewind/bq->base)*bq->base; +} + +void pa_memblockq_rewind(pa_memblockq *bq, size_t length) { + pa_assert(bq); + pa_assert(length % bq->base == 0); + + bq->read_index -= length; + bq->missing -= length; +} + +int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) { + + pa_assert(bq); + pa_assert(source); + + pa_memblockq_prebuf_disable(bq); + + for (;;) { + pa_memchunk chunk; + + if (pa_memblockq_peek(source, &chunk) < 0) + return 0; + + pa_assert(chunk.length > 0); + + if (chunk.memblock) { + + if (pa_memblockq_push_align(bq, &chunk) < 0) { + pa_memblock_unref(chunk.memblock); + return -1; + } + + pa_memblock_unref(chunk.memblock); + } else + pa_memblockq_seek(bq, chunk.length, PA_SEEK_RELATIVE); + + pa_memblockq_drop(bq, chunk.length); + } +} + +void pa_memblockq_willneed(pa_memblockq *bq) { + struct list_item *q; + + pa_assert(bq); + + fix_current_read(bq); + + for (q = bq->current_read; q; q = q->next) + pa_memchunk_will_need(&q->chunk); +} + +void pa_memblockq_set_silence(pa_memblockq *bq, pa_memblock *silence) { + pa_assert(bq); + + if (bq->silence) + pa_memblock_unref(bq->silence); + + bq->silence = silence ? pa_memblock_ref(silence) : NULL; +} + +pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq) { + pa_assert(bq); + + return !bq->blocks; +} diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h index 46637f10..8610a1aa 100644 --- a/src/pulsecore/memblockq.h +++ b/src/pulsecore/memblockq.h @@ -62,6 +62,8 @@ typedef struct pa_memblockq pa_memblockq; - minreq: pa_memblockq_missing() will only return values greater than this value. Pass 0 for the default. + - maxrewind: how many bytes of history to keep in the queue + - silence: return this memblock when reading unitialized data */ pa_memblockq* pa_memblockq_new( @@ -71,6 +73,7 @@ pa_memblockq* pa_memblockq_new( size_t base, size_t prebuf, size_t minreq, + size_t maxrewind, pa_memblock *silence); void pa_memblockq_free(pa_memblockq*bq); @@ -95,7 +98,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk); void pa_memblockq_drop(pa_memblockq *bq, size_t length); /* Test if the pa_memblockq is currently readable, that is, more data than base */ -int pa_memblockq_is_readable(pa_memblockq *bq); +pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq); /* Return the length of the queue in bytes */ size_t pa_memblockq_get_length(pa_memblockq *bq); @@ -107,6 +110,9 @@ size_t pa_memblockq_missing(pa_memblockq *bq); * this function, reset the internal counter to 0. */ size_t pa_memblockq_pop_missing(pa_memblockq *bq); +/* Directly moves the data from the source memblockq into bq */ +int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source); + /* Returns the minimal request value */ size_t pa_memblockq_get_minreq(pa_memblockq *bq); @@ -125,10 +131,8 @@ int64_t pa_memblockq_get_read_index(pa_memblockq *bq); /* Return the current write index */ int64_t pa_memblockq_get_write_index(pa_memblockq *bq); -/* Shorten the pa_memblockq to the specified length by dropping data - * at the read end of the queue. The read index is increased until the - * queue has the specified length */ -void pa_memblockq_shorten(pa_memblockq *bq, size_t length); +/* Rewind the read index. If the history is shorter than the specified length we'll point to silence afterwards. */ +void pa_memblockq_rewind(pa_memblockq *bq, size_t length); /* Ignore prebuf for now */ void pa_memblockq_prebuf_disable(pa_memblockq *bq); @@ -142,10 +146,20 @@ size_t pa_memblockq_get_maxlength(pa_memblockq *bq); /* Return the prebuffer length in bytes */ size_t pa_memblockq_get_prebuf(pa_memblockq *bq); -/* Change metrics. */ -void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); -void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); -void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); +/* Change metrics. Always call in order. */ +void pa_memblockq_set_maxlength(pa_memblockq *memblockq, size_t maxlength); /* might modify tlength, prebuf, minreq too */ +void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might modify minreq, too */ +void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); /* might modify minreq, too */ void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq); +void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t rewind); /* Set the maximum history size */ +void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memblock *silence); + +/* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */ +void pa_memblockq_willneed(pa_memblockq *bq); + +/* Check whether the memblockq is completely empty, i.e. no data + * neither left nor right of the read pointer, and hence no buffered + * data for the future nor data in the backlog. */ +pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq); #endif diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c index fe520384..1b0977d7 100644 --- a/src/pulsecore/namereg.c +++ b/src/pulsecore/namereg.c @@ -179,7 +179,7 @@ void pa_namereg_unregister(pa_core *c, const char *name) { pa_xfree(e); } -void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload) { +void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) { struct namereg_entry *e; uint32_t idx; pa_assert(c); diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h index d0db9e81..0f5b4d4d 100644 --- a/src/pulsecore/namereg.h +++ b/src/pulsecore/namereg.h @@ -39,7 +39,7 @@ void pa_namereg_free(pa_core *c); const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, int fail); void pa_namereg_unregister(pa_core *c, const char *name); -void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, int autoload); +void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload); int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type); const char *pa_namereg_get_default_sink_name(pa_core *c); diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 3ab2361b..51f2b309 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -126,7 +126,7 @@ enum { PA_COMMAND_SUSPEND_SINK, PA_COMMAND_SUSPEND_SOURCE, - /* Supported since protocol v13 (0.9.8) */ + /* Supported since protocol v12 (0.9.8) */ PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR, PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR, @@ -139,6 +139,14 @@ enum { PA_COMMAND_PLAYBACK_STREAM_MOVED, PA_COMMAND_RECORD_STREAM_MOVED, + /* Supported since protocol v13 (0.9.10) */ + PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST, + PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST, + PA_COMMAND_UPDATE_CLIENT_PROPLIST, + PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST, + PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST, + PA_COMMAND_REMOVE_CLIENT_PROPLIST, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 5d3c2d39..bad60ae5 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "play-memblockq.h" @@ -59,7 +60,6 @@ static void memblockq_stream_unlink(memblockq_stream *u) { return; pa_sink_input_unlink(u->sink_input); - pa_sink_input_unref(u->sink_input); u->sink_input = NULL; @@ -70,8 +70,6 @@ static void memblockq_stream_free(pa_object *o) { memblockq_stream *u = MEMBLOCKQ_STREAM(o); pa_assert(u); - memblockq_stream_unlink(u); - if (u->memblockq) pa_memblockq_free(u->memblockq); @@ -92,15 +90,19 @@ static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata } static void sink_input_kill_cb(pa_sink_input *i) { + memblockq_stream *u; + pa_sink_input_assert_ref(i); + u = MEMBLOCKQ_STREAM(i->userdata); + memblockq_stream_assert_ref(u); - memblockq_stream_unlink(MEMBLOCKQ_STREAM(i->userdata)); + memblockq_stream_unlink(u); } -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { memblockq_stream *u; - pa_assert(i); + pa_sink_input_assert_ref(i); pa_assert(chunk); u = MEMBLOCKQ_STREAM(i->userdata); memblockq_stream_assert_ref(u); @@ -109,36 +111,56 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun return -1; if (pa_memblockq_peek(u->memblockq, chunk) < 0) { - pa_memblockq_free(u->memblockq); - u->memblockq = NULL; - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); + + if (pa_sink_input_safe_to_remove(i)) { + + pa_memblockq_free(u->memblockq); + u->memblockq = NULL; + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); + } + return -1; } + pa_memblockq_drop(u->memblockq, chunk->length); + return 0; } -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { + memblockq_stream *u; + + pa_sink_input_assert_ref(i); + pa_assert(nbytes > 0); + u = MEMBLOCKQ_STREAM(i->userdata); + memblockq_stream_assert_ref(u); + + if (!u->memblockq) + return; + + pa_memblockq_rewind(u->memblockq, nbytes); +} + +static void sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes) { memblockq_stream *u; - pa_assert(i); - pa_assert(length > 0); + pa_sink_input_assert_ref(i); u = MEMBLOCKQ_STREAM(i->userdata); memblockq_stream_assert_ref(u); if (!u->memblockq) return; - pa_memblockq_drop(u->memblockq, length); + pa_memblockq_set_maxrewind(u->memblockq, nbytes); } pa_sink_input* pa_memblockq_sink_input_new( pa_sink *sink, - const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_memblockq *q, - pa_cvolume *volume) { + pa_cvolume *volume, + pa_proplist *p) { memblockq_stream *u = NULL; pa_sink_input_new_data data; @@ -149,41 +171,35 @@ pa_sink_input* pa_memblockq_sink_input_new( /* We allow creating this stream with no q set, so that it can be * filled in later */ - if (q && pa_memblockq_get_length(q) <= 0) { - pa_memblockq_free(q); - return NULL; - } - - if (volume && pa_cvolume_is_muted(volume)) { - pa_memblockq_free(q); - return NULL; - } - u = pa_msgobject_new(memblockq_stream); u->parent.parent.free = memblockq_stream_free; u->parent.process_msg = memblockq_stream_process_msg; u->core = sink->core; u->sink_input = NULL; - u->memblockq = q; + u->memblockq = NULL; pa_sink_input_new_data_init(&data); data.sink = sink; - data.name = name; data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_channel_map(&data, map); pa_sink_input_new_data_set_volume(&data, volume); + pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); - if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0))) + u->sink_input = pa_sink_input_new(sink->core, &data, 0); + pa_sink_input_new_data_done(&data); + + if (!u->sink_input) goto fail; - u->sink_input->peek = sink_input_peek_cb; - u->sink_input->drop = sink_input_drop_cb; + u->sink_input->pop = sink_input_pop_cb; + u->sink_input->rewind = sink_input_rewind_cb; + u->sink_input->set_max_rewind = sink_input_set_max_rewind; u->sink_input->kill = sink_input_kill_cb; u->sink_input->userdata = u; if (q) - pa_memblockq_prebuf_disable(q); + pa_memblockq_sink_input_set_queue(u->sink_input, q); /* The reference to u is dangling here, because we want * to keep this stream around until it is fully played. */ @@ -202,11 +218,12 @@ fail: int pa_play_memblockq( pa_sink *sink, - const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_memblockq *q, - pa_cvolume *volume) { + pa_cvolume *volume, + pa_proplist *p, + uint32_t *sink_input_index) { pa_sink_input *i; @@ -214,10 +231,14 @@ int pa_play_memblockq( pa_assert(ss); pa_assert(q); - if (!(i = pa_memblockq_sink_input_new(sink, name, ss, map, q, volume))) + if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p))) return -1; pa_sink_input_put(i); + + if (sink_input_index) + *sink_input_index = i->index; + pa_sink_input_unref(i); return 0; @@ -232,5 +253,20 @@ void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) { if (u->memblockq) pa_memblockq_free(u->memblockq); - u->memblockq = q; + + if ((u->memblockq = q)) { + pa_memblock *silence; + + pa_memblockq_set_prebuf(q, 0); + + silence = pa_silence_memblock_new( + i->sink->core->mempool, + &i->sample_spec, + i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0); + + pa_memblockq_set_silence(q, silence); + pa_memblock_unref(silence); + + pa_memblockq_willneed(q); + } } diff --git a/src/pulsecore/play-memblockq.h b/src/pulsecore/play-memblockq.h index d8790316..9ecf7700 100644 --- a/src/pulsecore/play-memblockq.h +++ b/src/pulsecore/play-memblockq.h @@ -29,20 +29,21 @@ pa_sink_input* pa_memblockq_sink_input_new( pa_sink *sink, - const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_memblockq *q, - pa_cvolume *volume); + pa_cvolume *volume, + pa_proplist *p); void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q); int pa_play_memblockq( pa_sink *sink, - const char *name, const pa_sample_spec *ss, const pa_channel_map *map, pa_memblockq *q, - pa_cvolume *cvolume); + pa_cvolume *cvolume, + pa_proplist *p, + uint32_t *sink_input_index); #endif diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c index 6aaec567..c528b1d0 100644 --- a/src/pulsecore/play-memchunk.c +++ b/src/pulsecore/play-memchunk.c @@ -34,163 +34,33 @@ #include #include #include +#include #include "play-memchunk.h" -typedef struct memchunk_stream { - pa_msgobject parent; - pa_core *core; - pa_sink_input *sink_input; - pa_memchunk memchunk; -} memchunk_stream; - -enum { - MEMCHUNK_STREAM_MESSAGE_UNLINK, -}; - -PA_DECLARE_CLASS(memchunk_stream); -#define MEMCHUNK_STREAM(o) (memchunk_stream_cast(o)) -static PA_DEFINE_CHECK_TYPE(memchunk_stream, pa_msgobject); - -static void memchunk_stream_unlink(memchunk_stream *u) { - pa_assert(u); - - if (!u->sink_input) - return; - - pa_sink_input_unlink(u->sink_input); - - pa_sink_input_unref(u->sink_input); - u->sink_input = NULL; - - memchunk_stream_unref(u); -} - -static void memchunk_stream_free(pa_object *o) { - memchunk_stream *u = MEMCHUNK_STREAM(o); - pa_assert(u); - - memchunk_stream_unlink(u); - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - - pa_xfree(u); -} - -static int memchunk_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { - memchunk_stream *u = MEMCHUNK_STREAM(o); - memchunk_stream_assert_ref(u); - - switch (code) { - case MEMCHUNK_STREAM_MESSAGE_UNLINK: - memchunk_stream_unlink(u); - break; - } - - return 0; -} - -static void sink_input_kill_cb(pa_sink_input *i) { - pa_sink_input_assert_ref(i); - - memchunk_stream_unlink(MEMCHUNK_STREAM(i->userdata)); -} - -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { - memchunk_stream *u; - - pa_assert(i); - pa_assert(chunk); - u = MEMCHUNK_STREAM(i->userdata); - memchunk_stream_assert_ref(u); - - if (!u->memchunk.memblock) - return -1; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - u->memchunk.memblock = NULL; - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMCHUNK_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); - return -1; - } - - pa_assert(u->memchunk.memblock); - *chunk = u->memchunk; - pa_memblock_ref(chunk->memblock); - - return 0; -} - -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { - memchunk_stream *u; - - pa_assert(i); - pa_assert(length > 0); - u = MEMCHUNK_STREAM(i->userdata); - memchunk_stream_assert_ref(u); - - if (length < u->memchunk.length) { - u->memchunk.length -= length; - u->memchunk.index += length; - } else - u->memchunk.length = 0; -} - int pa_play_memchunk( pa_sink *sink, - const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, - pa_cvolume *volume) { + pa_cvolume *volume, + pa_proplist *p, + uint32_t *sink_input_index) { - memchunk_stream *u = NULL; - pa_sink_input_new_data data; + pa_memblockq *q; + int r; pa_assert(sink); pa_assert(ss); pa_assert(chunk); - if (volume && pa_cvolume_is_muted(volume)) - return 0; - - pa_memchunk_will_need(chunk); - - u = pa_msgobject_new(memchunk_stream); - u->parent.parent.free = memchunk_stream_free; - u->parent.process_msg = memchunk_stream_process_msg; - u->core = sink->core; - u->memchunk = *chunk; - pa_memblock_ref(u->memchunk.memblock); - - pa_sink_input_new_data_init(&data); - data.sink = sink; - data.driver = __FILE__; - data.name = name; - pa_sink_input_new_data_set_sample_spec(&data, ss); - pa_sink_input_new_data_set_channel_map(&data, map); - pa_sink_input_new_data_set_volume(&data, volume); - - if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0))) - goto fail; - - u->sink_input->peek = sink_input_peek_cb; - u->sink_input->drop = sink_input_drop_cb; - u->sink_input->kill = sink_input_kill_cb; - u->sink_input->userdata = u; + q = pa_memblockq_new(0, chunk->length, 0, pa_frame_size(ss), 0, 0, 0, NULL); + pa_assert_se(pa_memblockq_push(q, chunk) >= 0); - pa_sink_input_put(u->sink_input); - - /* The reference to u is dangling here, because we want to keep - * this stream around until it is fully played. */ + if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, sink_input_index)) < 0) { + pa_memblockq_free(q); + return r; + } return 0; - -fail: - if (u) - memchunk_stream_unref(u); - - return -1; } - diff --git a/src/pulsecore/play-memchunk.h b/src/pulsecore/play-memchunk.h index 5afb094c..f7c9d178 100644 --- a/src/pulsecore/play-memchunk.h +++ b/src/pulsecore/play-memchunk.h @@ -29,10 +29,11 @@ int pa_play_memchunk( pa_sink *sink, - const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, - pa_cvolume *cvolume); + pa_cvolume *cvolume, + pa_proplist *p, + uint32_t *sink_input_index); #endif diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 004e535e..972e8e1a 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -149,8 +149,7 @@ typedef struct proto_handler { const char *description; } esd_proto_handler_info_t; -static void sink_input_drop_cb(pa_sink_input *i, size_t length); -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk); +static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk); static void sink_input_kill_cb(pa_sink_input *i); static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); static pa_usec_t source_output_get_latency_cb(pa_source_output *o); @@ -410,14 +409,16 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques pa_assert(!c->sink_input && !c->input_memblockq); pa_sink_input_new_data_init(&sdata); - sdata.sink = sink; sdata.driver = __FILE__; - sdata.name = c->client->name; - pa_sink_input_new_data_set_sample_spec(&sdata, &ss); sdata.module = c->protocol->module; sdata.client = c->client; + sdata.sink = sink; + pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist); + pa_sink_input_new_data_set_sample_spec(&sdata, &ss); c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0); + pa_sink_input_new_data_done(&sdata); + CHECK_VALIDITY(c->sink_input, "Failed to create sink input."); l = (size_t) (pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS); @@ -428,13 +429,13 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques pa_frame_size(&ss), (size_t) -1, l/PLAYBACK_BUFFER_FRAGMENTS, + 0, NULL); pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS; c->sink_input->parent.process_msg = sink_input_process_msg; - c->sink_input->peek = sink_input_peek_cb; - c->sink_input->drop = sink_input_drop_cb; + c->sink_input->pop = sink_input_pop_cb; c->sink_input->kill = sink_input_kill_cb; c->sink_input->userdata = c; @@ -509,14 +510,16 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi pa_assert(!c->output_memblockq && !c->source_output); pa_source_output_new_data_init(&sdata); - sdata.source = source; sdata.driver = __FILE__; - sdata.name = c->client->name; - pa_source_output_new_data_set_sample_spec(&sdata, &ss); sdata.module = c->protocol->module; sdata.client = c->client; + sdata.source = source; + pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist); + pa_source_output_new_data_set_sample_spec(&sdata, &ss); + + c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0); + pa_source_output_new_data_done(&sdata); - c->source_output = pa_source_output_new(c->protocol->core, &sdata, 9); CHECK_VALIDITY(c->source_output, "Failed to create source_output."); l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); @@ -527,6 +530,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi pa_frame_size(&ss), 1, 0, + 0, NULL); pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2); @@ -638,8 +642,8 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da memset(name, 0, ESD_NAME_MAX); /* don't leak old data */ if (conn->original_name) strncpy(name, conn->original_name, ESD_NAME_MAX); - else if (conn->client && conn->client->name) - strncpy(name, conn->client->name, ESD_NAME_MAX); + else if (conn->client && pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME)) + strncpy(name, pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME), ESD_NAME_MAX); connection_write(c, name, ESD_NAME_MAX); /* rate */ @@ -800,7 +804,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque c->state = ESD_CACHING_SAMPLE; - pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, &idx); + pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, c->client->proplist, &idx); idx += 1; connection_write(c, &idx, sizeof(uint32_t)); @@ -851,7 +855,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con pa_sink *sink; if ((sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, 1))) - if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM) >= 0) + if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0) ok = idx + 1; } else { pa_assert(request == ESD_PROTO_SAMPLE_FREE); @@ -992,7 +996,7 @@ static int do_read(connection *c) { uint32_t idx; c->scache.memchunk.index = 0; - pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, &idx); + pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx); pa_memblock_unref(c->scache.memchunk.memblock); c->scache.memchunk.memblock = NULL; @@ -1237,7 +1241,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int } /* Called from thread context */ -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { connection*c; int r; @@ -1246,32 +1250,25 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun connection_assert_ref(c); pa_assert(chunk); - if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0 && c->dead) - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL); + if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0) { - return r; -} - -/* Called from thread context */ -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { - connection*c; - size_t old, new; - - pa_assert(i); - c = CONNECTION(i->userdata); - connection_assert_ref(c); - pa_assert(length); - /* pa_log("DROP"); */ + if (c->dead && pa_sink_input_safe_to_remove(i)) + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL); + } else { + size_t old, new; - old = pa_memblockq_missing(c->input_memblockq); - pa_memblockq_drop(c->input_memblockq, length); - new = pa_memblockq_missing(c->input_memblockq); + old = pa_memblockq_missing(c->input_memblockq); + pa_memblockq_drop(c->input_memblockq, chunk->length); + new = pa_memblockq_missing(c->input_memblockq); - if (new > old) { - if (pa_atomic_add(&c->playback.missing, new - old) <= 0) - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); + if (new > old) { + if (pa_atomic_add(&c->playback.missing, new - old) <= 0) + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); + } } + + return r; } static void sink_input_kill_cb(pa_sink_input *i) { @@ -1349,7 +1346,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname); c->client = pa_client_new(p->core, __FILE__, cname); - c->client->owner = p->module; + c->client->module = p->module; c->client->kill = client_kill_cb; c->client->userdata = c; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 4f582798..811cc805 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -98,17 +98,17 @@ typedef struct playback_stream { pa_sink_input *sink_input; pa_memblockq *memblockq; - int drain_request; + pa_bool_t drain_request; uint32_t drain_tag; uint32_t syncid; - int underrun; + pa_bool_t underrun; pa_atomic_t missing; size_t minreq; /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */ int64_t read_index, write_index; - size_t resampled_chunk_length; + size_t render_memblockq_length; } playback_stream; typedef struct upload_stream { @@ -122,12 +122,13 @@ typedef struct upload_stream { char *name; pa_sample_spec sample_spec; pa_channel_map channel_map; + pa_proplist *proplist; } upload_stream; struct connection { pa_msgobject parent; - int authorized; + pa_bool_t authorized; uint32_t version; pa_protocol_native *protocol; pa_client *client; @@ -162,11 +163,11 @@ static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject); struct pa_protocol_native { pa_module *module; pa_core *core; - int public; + pa_bool_t public; pa_socket_server *server; pa_idxset *connections; uint8_t auth_cookie[PA_NATIVE_COOKIE_LENGTH]; - int auth_cookie_in_property; + pa_bool_t auth_cookie_in_property; #ifdef HAVE_CREDS char *auth_group; #endif @@ -199,8 +200,7 @@ enum { CONNECTION_MESSAGE_REVOKE }; -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk); -static void sink_input_drop_cb(pa_sink_input *i, size_t length); +static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk); static void sink_input_kill_cb(pa_sink_input *i); static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend); static void sink_input_moved_cb(pa_sink_input *i); @@ -254,6 +254,8 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_ERROR] = NULL, @@ -335,7 +337,15 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr, [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate, - [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate + [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate, + + [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = command_update_proplist, + [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = command_update_proplist, + [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = command_update_proplist, + + [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = command_remove_proplist, + [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist, + [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist, }; /* structure management */ @@ -359,6 +369,9 @@ static void upload_stream_free(pa_object *o) { pa_xfree(s->name); + if (s->proplist) + pa_proplist_free(s->proplist); + if (s->memchunk.memblock) pa_memblock_unref(s->memchunk.memblock); @@ -369,7 +382,9 @@ static upload_stream* upload_stream_new( connection *c, const pa_sample_spec *ss, const pa_channel_map *map, - const char *name, size_t length) { + const char *name, + size_t length, + pa_proplist *p) { upload_stream *s; @@ -377,6 +392,7 @@ static upload_stream* upload_stream_new( pa_assert(ss); pa_assert(name); pa_assert(length > 0); + pa_assert(p); s = pa_msgobject_new(upload_stream); s->parent.parent.parent.free = upload_stream_free; @@ -386,6 +402,8 @@ static upload_stream* upload_stream_new( s->name = pa_xstrdup(name); pa_memchunk_reset(&s->memchunk); s->length = length; + s->proplist = pa_proplist_copy(p); + pa_proplist_update(s->proplist, PA_UPDATE_MERGE, c->client->proplist); pa_idxset_put(c->output_streams, s, &s->index); @@ -452,7 +470,8 @@ static record_stream* record_stream_new( const char *name, uint32_t *maxlength, uint32_t fragment_size, - pa_source_output_flags_t flags) { + pa_source_output_flags_t flags, + pa_proplist *p) { record_stream *s; pa_source_output *source_output; @@ -464,17 +483,24 @@ static record_stream* record_stream_new( pa_assert(name); pa_assert(maxlength); pa_assert(*maxlength > 0); + pa_assert(p); pa_source_output_new_data_init(&data); + + pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); + pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); + data.driver = __FILE__; data.module = c->protocol->module; data.client = c->client; data.source = source; - data.driver = __FILE__; - data.name = name; pa_source_output_new_data_set_sample_spec(&data, ss); pa_source_output_new_data_set_channel_map(&data, map); - if (!(source_output = pa_source_output_new(c->protocol->core, &data, flags))) + source_output = pa_source_output_new(c->protocol->core, &data, flags); + + pa_source_output_new_data_done(&data); + + if (!source_output) return NULL; s = pa_msgobject_new(record_stream); @@ -496,6 +522,7 @@ static record_stream* record_stream_new( base = pa_frame_size(&s->source_output->sample_spec), 1, 0, + 0, NULL); *maxlength = pa_memblockq_get_maxlength(s->memblockq); @@ -633,7 +660,8 @@ static playback_stream* playback_stream_new( pa_cvolume *volume, uint32_t syncid, uint32_t *missing, - pa_sink_input_flags_t flags) { + pa_sink_input_flags_t flags, + pa_proplist *p) { playback_stream *s, *ssync; pa_sink_input *sink_input; @@ -646,6 +674,7 @@ static playback_stream* playback_stream_new( pa_assert(ss); pa_assert(name); pa_assert(maxlength); + pa_assert(p); /* Find syncid group */ for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) { @@ -667,17 +696,23 @@ static playback_stream* playback_stream_new( } pa_sink_input_new_data_init(&data); - data.sink = sink; + + pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); + pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); data.driver = __FILE__; - data.name = name; + data.module = c->protocol->module; + data.client = c->client; + data.sink = sink; pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_channel_map(&data, map); pa_sink_input_new_data_set_volume(&data, volume); - data.module = c->protocol->module; - data.client = c->client; data.sync_base = ssync ? ssync->sink_input : NULL; - if (!(sink_input = pa_sink_input_new(c->protocol->core, &data, flags))) + sink_input = pa_sink_input_new(c->protocol->core, &data, flags); + + pa_sink_input_new_data_done(&data); + + if (!sink_input) return NULL; s = pa_msgobject_new(playback_stream); @@ -686,11 +721,10 @@ static playback_stream* playback_stream_new( s->connection = c; s->syncid = syncid; s->sink_input = sink_input; - s->underrun = 1; + s->underrun = TRUE; s->sink_input->parent.process_msg = sink_input_process_msg; - s->sink_input->peek = sink_input_peek_cb; - s->sink_input->drop = sink_input_drop_cb; + s->sink_input->pop = sink_input_pop_cb; s->sink_input->kill = sink_input_kill_cb; s->sink_input->moved = sink_input_moved_cb; s->sink_input->suspend = sink_input_suspend_cb; @@ -707,6 +741,7 @@ static playback_stream* playback_stream_new( pa_frame_size(&s->sink_input->sample_spec), *prebuf, *minreq, + 0, silence); pa_memblock_unref(silence); @@ -722,7 +757,7 @@ static playback_stream* playback_stream_new( s->minreq = pa_memblockq_get_minreq(s->memblockq); pa_atomic_store(&s->missing, 0); - s->drain_request = 0; + s->drain_request = FALSE; pa_idxset_put(c->output_streams, s, &s->index); @@ -909,7 +944,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int request_bytes(s); - s->underrun = 0; + s->underrun = FALSE; return 0; } @@ -921,7 +956,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL); else { s->drain_tag = PA_PTR_TO_UINT(userdata); - s->drain_request = 1; + s->drain_request = TRUE; } request_bytes(s); @@ -953,21 +988,21 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int } func(s->memblockq); - s->underrun = 0; + s->underrun = FALSE; request_bytes(s); /* Do the same for all other members in the sync group */ for (isync = i->sync_prev; isync; isync = isync->sync_prev) { playback_stream *ssync = PLAYBACK_STREAM(isync->userdata); func(ssync->memblockq); - ssync->underrun = 0; + ssync->underrun = FALSE; request_bytes(ssync); } for (isync = i->sync_next; isync; isync = isync->sync_next) { playback_stream *ssync = PLAYBACK_STREAM(isync->userdata); func(ssync->memblockq); - ssync->underrun = 0; + ssync->underrun = FALSE; request_bytes(ssync); } @@ -978,7 +1013,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int s->read_index = pa_memblockq_get_read_index(s->memblockq); s->write_index = pa_memblockq_get_write_index(s->memblockq); - s->resampled_chunk_length = s->sink_input->thread_info.resampled_chunk.memblock ? s->sink_input->thread_info.resampled_chunk.length : 0; + s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq); return 0; case PA_SINK_INPUT_MESSAGE_SET_STATE: @@ -1002,7 +1037,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int } /* Called from thread context */ -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { playback_stream *s; pa_sink_input_assert_ref(i); @@ -1010,42 +1045,23 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun playback_stream_assert_ref(s); pa_assert(chunk); - if (pa_memblockq_get_length(s->memblockq) <= 0 && !s->underrun) { - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL); - s->underrun = 1; - } - if (pa_memblockq_peek(s->memblockq, chunk) < 0) { -/* pa_log("peek: failure"); */ - return -1; - } - -/* pa_log("peek: %u", chunk->length); */ - request_bytes(s); - - return 0; -} - -/* Called from thread context */ -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { - playback_stream *s; - - pa_sink_input_assert_ref(i); - s = PLAYBACK_STREAM(i->userdata); - playback_stream_assert_ref(s); - pa_assert(length > 0); - - pa_memblockq_drop(s->memblockq, length); + if (s->drain_request && pa_sink_input_safe_to_remove(i)) { + s->drain_request = FALSE; + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL); + } else if (!s->underrun) { + s->underrun = TRUE; + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL); + } - if (s->drain_request && !pa_memblockq_is_readable(s->memblockq)) { - s->drain_request = 0; - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL); + return -1; } + pa_memblockq_drop(s->memblockq, chunk->length); request_bytes(s); -/* pa_log("after_drop: %u %u", pa_memblockq_get_length(s->memblockq), pa_memblockq_is_readable(s->memblockq)); */ + return 0; } /* Called from main context */ @@ -1207,7 +1223,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC connection *c = CONNECTION(userdata); playback_stream *s; uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing; - const char *name, *sink_name; + const char *name = NULL, *sink_name; pa_sample_spec ss; pa_channel_map map; pa_tagstruct *reply; @@ -1216,29 +1232,45 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC int corked; int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0; pa_sink_input_flags_t flags = 0; + pa_proplist *p; connection_assert_ref(c); pa_assert(t); - if (pa_tagstruct_get( - t, - PA_TAG_STRING, &name, - PA_TAG_SAMPLE_SPEC, &ss, - PA_TAG_CHANNEL_MAP, &map, - PA_TAG_U32, &sink_index, - PA_TAG_STRING, &sink_name, - PA_TAG_U32, &maxlength, - PA_TAG_BOOLEAN, &corked, - PA_TAG_U32, &tlength, - PA_TAG_U32, &prebuf, - PA_TAG_U32, &minreq, - PA_TAG_U32, &syncid, - PA_TAG_CVOLUME, &volume, - PA_TAG_INVALID) < 0 || !name) { + if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) || + pa_tagstruct_get( + t, + PA_TAG_SAMPLE_SPEC, &ss, + PA_TAG_CHANNEL_MAP, &map, + PA_TAG_U32, &sink_index, + PA_TAG_STRING, &sink_name, + PA_TAG_U32, &maxlength, + PA_TAG_BOOLEAN, &corked, + PA_TAG_U32, &tlength, + PA_TAG_U32, &prebuf, + PA_TAG_U32, &minreq, + PA_TAG_U32, &syncid, + PA_TAG_CVOLUME, &volume, + PA_TAG_INVALID) < 0) { + protocol_error(c); return; } + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(sink_name)), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID); + + p = pa_proplist_new(); + + if (name) + pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name); + if (c->version >= 12) { /* Since 0.9.8 the user can ask for a couple of additional flags */ @@ -1249,32 +1281,43 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC pa_tagstruct_get_boolean(t, &fix_channels) < 0 || pa_tagstruct_get_boolean(t, &no_move) < 0 || pa_tagstruct_get_boolean(t, &variable_rate) < 0) { + + protocol_error(c); + pa_proplist_free(p); + return; + } + } + + if (c->version >= 13) { + + if (pa_tagstruct_get_proplist(t, p) < 0) { protocol_error(c); + pa_proplist_free(p); return; } } if (!pa_tagstruct_eof(t)) { protocol_error(c); + pa_proplist_free(p); return; } - CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); - CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID); - if (sink_index != PA_INVALID_INDEX) { - sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); - CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); + + if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + pa_proplist_free(p); + return; + } + } else if (sink_name) { - sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1); - CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); + + if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + pa_proplist_free(p); + return; + } } flags = @@ -1287,7 +1330,9 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0); - s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, &missing, flags); + s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, &missing, flags, p); + pa_proplist_free(p); + CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); reply = reply_new(tag); @@ -1395,11 +1440,12 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ int corked; int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0; pa_source_output_flags_t flags = 0; + pa_proplist *p; connection_assert_ref(c); pa_assert(t); - if (pa_tagstruct_gets(t, &name) < 0 || + if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) || pa_tagstruct_get_sample_spec(t, &ss) < 0 || pa_tagstruct_get_channel_map(t, &map) < 0 || pa_tagstruct_getu32(t, &source_index) < 0 || @@ -1411,6 +1457,19 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ return; } + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID); + + p = pa_proplist_new(); + + if (name) + pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name); + if (c->version >= 12) { /* Since 0.9.8 the user can ask for a couple of additional flags */ @@ -1421,16 +1480,45 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ pa_tagstruct_get_boolean(t, &fix_channels) < 0 || pa_tagstruct_get_boolean(t, &no_move) < 0 || pa_tagstruct_get_boolean(t, &variable_rate) < 0) { + + protocol_error(c); + pa_proplist_free(p); + return; + } + } + + if (c->version >= 13) { + + if (pa_tagstruct_get_proplist(t, p) < 0) { protocol_error(c); + pa_proplist_free(p); return; } } if (!pa_tagstruct_eof(t)) { protocol_error(c); + pa_proplist_free(p); return; } + if (source_index != PA_INVALID_INDEX) { + + if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + pa_proplist_free(p); + return; + } + + } else if (source_name) { + + if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + pa_proplist_free(p); + return; + } + } + flags = (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) | (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) | @@ -1441,24 +1529,9 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); - CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); - CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID); - - if (source_index != PA_INVALID_INDEX) { - source = pa_idxset_get_by_index(c->protocol->core->sources, source_index); - CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY); - } else if (source_name) { - source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1); - CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY); - } + s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, flags, p); + pa_proplist_free(p); - s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, flags); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); reply = reply_new(tag); @@ -1511,6 +1584,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t connection *c = CONNECTION(userdata); const void*cookie; pa_tagstruct *reply; + char tmp[16]; connection_assert_ref(c); pa_assert(t); @@ -1528,6 +1602,9 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t return; } + pa_snprintf(tmp, sizeof(tmp), "%u", c->version); + pa_proplist_sets(c->client->proplist, "native-protocol.version", tmp); + if (!c->authorized) { int success = 0; @@ -1579,7 +1656,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t return; } - c->authorized = 1; + c->authorized = TRUE; if (c->auth_timeout_event) { c->protocol->core->mainloop->time_free(c->auth_timeout_event); c->auth_timeout_event = NULL; @@ -1607,21 +1684,42 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { connection *c = CONNECTION(userdata); - const char *name; + const char *name = NULL; + pa_proplist *p; + pa_tagstruct *reply; connection_assert_ref(c); pa_assert(t); - if (pa_tagstruct_gets(t, &name) < 0 || + p = pa_proplist_new(); + + if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) || + (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) || !pa_tagstruct_eof(t)) { + protocol_error(c); + pa_proplist_free(p); return; } - CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID); + if (name) + if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); + pa_proplist_free(p); + return; + } - pa_client_set_name(c->client, name); - pa_pstream_send_simple_ack(c->pstream, tag); + pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p); + pa_proplist_free(p); + + pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); + + reply = reply_new(tag); + + if (c->version >= 13) + pa_tagstruct_putu32(reply, c->client->index); + + pa_pstream_send_tagstruct(c->pstream, reply); } static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -1737,7 +1835,7 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ reply = reply_new(tag); latency = pa_sink_get_latency(s->sink_input->sink); - latency += pa_bytes_to_usec(s->resampled_chunk_length, &s->sink_input->sample_spec); + latency += pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sample_spec); pa_tagstruct_put_usec(reply, latency); @@ -1786,19 +1884,19 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ connection *c = CONNECTION(userdata); upload_stream *s; uint32_t length; - const char *name; + const char *name = NULL; pa_sample_spec ss; pa_channel_map map; pa_tagstruct *reply; + pa_proplist *p; connection_assert_ref(c); pa_assert(t); - if (pa_tagstruct_gets(t, &name) < 0 || + if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) || pa_tagstruct_get_sample_spec(t, &ss) < 0 || pa_tagstruct_get_channel_map(t, &map) < 0 || - pa_tagstruct_getu32(t, &length) < 0 || - !pa_tagstruct_eof(t)) { + pa_tagstruct_getu32(t, &length) < 0) { protocol_error(c); return; } @@ -1809,9 +1907,24 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE); - CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); - s = upload_stream_new(c, &ss, &map, name, length); + if (c->version < 13) + CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); + + p = pa_proplist_new(); + + if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) { + protocol_error(c); + pa_proplist_free(p); + return; + } + + if (c->version < 13) + pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name); + + s = upload_stream_new(c, &ss, &map, name, length, p); + pa_proplist_free(p); + CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); reply = reply_new(tag); @@ -1841,7 +1954,7 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY); - if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, &idx) < 0) + if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0) pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL); else pa_pstream_send_simple_ack(c->pstream, tag); @@ -1855,20 +1968,23 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui pa_volume_t volume; pa_sink *sink; const char *name, *sink_name; + uint32_t idx; + pa_proplist *p; + pa_tagstruct *reply; connection_assert_ref(c); pa_assert(t); + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + if (pa_tagstruct_getu32(t, &sink_index) < 0 || pa_tagstruct_gets(t, &sink_name) < 0 || pa_tagstruct_getu32(t, &volume) < 0 || - pa_tagstruct_gets(t, &name) < 0 || - !pa_tagstruct_eof(t)) { + pa_tagstruct_gets(t, &name) < 0) { protocol_error(c); return; } - CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); CHECK_VALIDITY(c->pstream, sink_index != PA_INVALID_INDEX || !sink_name || (*sink_name && pa_utf8_valid(name)), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID); @@ -1879,12 +1995,29 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY); - if (pa_scache_play_item(c->protocol->core, name, sink, volume) < 0) { + p = pa_proplist_new(); + + if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) || + !pa_tagstruct_eof(t)) { + protocol_error(c); + pa_proplist_free(p); + return; + } + + if (pa_scache_play_item(c->protocol->core, name, sink, volume, p, &idx) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + pa_proplist_free(p); return; } - pa_pstream_send_simple_ack(c->pstream, tag); + pa_proplist_free(p); + + reply = reply_new(tag); + + if (c->version >= 13) + pa_tagstruct_putu32(reply, idx); + + pa_pstream_send_tagstruct(c->pstream, reply); } static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -1941,7 +2074,7 @@ static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) { t, PA_TAG_U32, sink->index, PA_TAG_STRING, sink->name, - PA_TAG_STRING, sink->description, + PA_TAG_STRING, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), PA_TAG_SAMPLE_SPEC, &fixed_ss, PA_TAG_CHANNEL_MAP, &sink->channel_map, PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX, @@ -1953,6 +2086,9 @@ static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) { PA_TAG_STRING, sink->driver, PA_TAG_U32, sink->flags, PA_TAG_INVALID); + + if (c->version >= 13) + pa_tagstruct_put_proplist(t, sink->proplist); } static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *source) { @@ -1967,7 +2103,7 @@ static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *sou t, PA_TAG_U32, source->index, PA_TAG_STRING, source->name, - PA_TAG_STRING, source->description, + PA_TAG_STRING, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), PA_TAG_SAMPLE_SPEC, &fixed_ss, PA_TAG_CHANNEL_MAP, &source->channel_map, PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX, @@ -1979,16 +2115,23 @@ static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *sou PA_TAG_STRING, source->driver, PA_TAG_U32, source->flags, PA_TAG_INVALID); + + if (c->version >= 13) + pa_tagstruct_put_proplist(t, source->proplist); } -static void client_fill_tagstruct(pa_tagstruct *t, pa_client *client) { +static void client_fill_tagstruct(connection *c, pa_tagstruct *t, pa_client *client) { pa_assert(t); pa_assert(client); pa_tagstruct_putu32(t, client->index); - pa_tagstruct_puts(t, client->name); - pa_tagstruct_putu32(t, client->owner ? client->owner->index : PA_INVALID_INDEX); + pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME))); + pa_tagstruct_putu32(t, client->module ? client->module->index : PA_INVALID_INDEX); pa_tagstruct_puts(t, client->driver); + + if (c->version >= 13) + pa_tagstruct_put_proplist(t, client->proplist); + } static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) { @@ -2011,7 +2154,7 @@ static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_in fixup_sample_spec(c, &fixed_ss, &s->sample_spec); pa_tagstruct_putu32(t, s->index); - pa_tagstruct_puts(t, s->name); + pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME))); pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX); pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX); pa_tagstruct_putu32(t, s->sink->index); @@ -2024,6 +2167,8 @@ static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_in pa_tagstruct_puts(t, s->driver); if (c->version >= 11) pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s)); + if (c->version >= 13) + pa_tagstruct_put_proplist(t, s->proplist); } static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) { @@ -2035,7 +2180,7 @@ static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sour fixup_sample_spec(c, &fixed_ss, &s->sample_spec); pa_tagstruct_putu32(t, s->index); - pa_tagstruct_puts(t, s->name); + pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME))); pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX); pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX); pa_tagstruct_putu32(t, s->source->index); @@ -2045,6 +2190,9 @@ static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sour pa_tagstruct_put_usec(t, pa_source_get_latency(s->source)); pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s))); pa_tagstruct_puts(t, s->driver); + + if (c->version >= 13) + pa_tagstruct_put_proplist(t, s->proplist); } static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entry *e) { @@ -2064,6 +2212,9 @@ static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entr pa_tagstruct_putu32(t, e->memchunk.length); pa_tagstruct_put_boolean(t, e->lazy); pa_tagstruct_puts(t, e->filename); + + if (c->version >= 13) + pa_tagstruct_put_proplist(t, e->proplist); } static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -2133,7 +2284,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u else if (source) source_fill_tagstruct(c, reply, source); else if (client) - client_fill_tagstruct(reply, client); + client_fill_tagstruct(c, reply, client); else if (module) module_fill_tagstruct(reply, module); else if (si) @@ -2188,7 +2339,7 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST) source_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) - client_fill_tagstruct(reply, p); + client_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) module_fill_tagstruct(reply, p); else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) @@ -2660,6 +2811,168 @@ static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command pa_pstream_send_simple_ack(c->pstream, tag); } +static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + connection *c = CONNECTION(userdata); + uint32_t idx; + uint32_t mode; + pa_proplist *p; + + connection_assert_ref(c); + pa_assert(t); + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + + p = pa_proplist_new(); + + if (command == PA_COMMAND_UPDATE_CLIENT_PROPLIST) { + + if (pa_tagstruct_getu32(t, &mode) < 0 || + pa_tagstruct_get_proplist(t, p) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + pa_proplist_free(p); + return; + } + + } else { + + if (pa_tagstruct_getu32(t, &idx) < 0 || + pa_tagstruct_getu32(t, &mode) < 0 || + pa_tagstruct_get_proplist(t, p) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + pa_proplist_free(p); + return; + } + } + + CHECK_VALIDITY(c->pstream, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, tag, PA_ERR_INVALID); + + if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) { + playback_stream *s; + + s = pa_idxset_get_by_index(c->output_streams, idx); + CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); + CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY); + + pa_proplist_update(s->sink_input->proplist, mode, p); + pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index); + + } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) { + record_stream *s; + + s = pa_idxset_get_by_index(c->record_streams, idx); + CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); + + pa_proplist_update(s->source_output->proplist, mode, p); + pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index); + } else { + pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST); + + pa_proplist_update(c->client->proplist, mode, p); + pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); + } + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + connection *c = CONNECTION(userdata); + uint32_t idx; + unsigned changed = 0; + pa_proplist *p; + pa_strlist *l = NULL; + + connection_assert_ref(c); + pa_assert(t); + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + + if (command != PA_COMMAND_REMOVE_CLIENT_PROPLIST) { + + if (pa_tagstruct_getu32(t, &idx) < 0) { + protocol_error(c); + return; + } + } + + if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) { + playback_stream *s; + + s = pa_idxset_get_by_index(c->output_streams, idx); + CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); + CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY); + + p = s->sink_input->proplist; + + } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) { + record_stream *s; + + s = pa_idxset_get_by_index(c->record_streams, idx); + CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); + + p = s->source_output->proplist; + } else { + pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST); + + p = c->client->proplist; + } + + for (;;) { + const char *k; + + if (pa_tagstruct_gets(t, &k) < 0) { + protocol_error(c); + pa_strlist_free(l); + return; + } + + if (!k) + break; + + l = pa_strlist_prepend(l, k); + } + + if (!pa_tagstruct_eof(t)) { + protocol_error(c); + pa_strlist_free(l); + return; + } + + for (;;) { + char *z; + + l = pa_strlist_pop(l, &z); + + if (!z) + break; + + changed += pa_proplist_unset(p, z) >= 0; + pa_xfree(z); + } + + pa_pstream_send_simple_ack(c->pstream, tag); + + if (changed) { + if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) { + playback_stream *s; + + s = pa_idxset_get_by_index(c->output_streams, idx); + pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index); + + } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) { + record_stream *s; + + s = pa_idxset_get_by_index(c->record_streams, idx); + pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index); + + } else { + pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST); + pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index); + } + } +} + static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { connection *c = CONNECTION(userdata); const char *s; @@ -3246,11 +3559,11 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo c->parent.parent.free = connection_free; c->parent.process_msg = connection_process_msg; - c->authorized = !!p->public; + c->authorized = p->public; if (!c->authorized && p->auth_ip_acl && pa_ip_acl_check(p->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) { pa_log_info("Client authenticated by IP ACL."); - c->authorized = 1; + c->authorized = TRUE; } if (!c->authorized) { @@ -3268,7 +3581,7 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo c->client = pa_client_new(p->core, __FILE__, cname); c->client->kill = client_kill_cb; c->client->userdata = c; - c->client->owner = p->module; + c->client->module = p->module; c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool); @@ -3301,12 +3614,12 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo static int load_key(pa_protocol_native*p, const char*fn) { pa_assert(p); - p->auth_cookie_in_property = 0; + p->auth_cookie_in_property = FALSE; if (!fn && pa_authkey_prop_get(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) { pa_log_info("using already loaded auth cookie."); pa_authkey_prop_ref(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME); - p->auth_cookie_in_property = 1; + p->auth_cookie_in_property = TRUE; return 0; } @@ -3319,7 +3632,7 @@ static int load_key(pa_protocol_native*p, const char*fn) { pa_log_info("loading cookie from disk."); if (pa_authkey_prop_put(p->core, PA_NATIVE_COOKIE_PROPERTY_NAME, p->auth_cookie, sizeof(p->auth_cookie)) >= 0) - p->auth_cookie_in_property = 1; + p->auth_cookie_in_property = TRUE; return 0; } diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 777def30..3ee2a058 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -343,7 +343,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int } /* Called from thread context */ -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { connection *c; int r; @@ -352,34 +352,25 @@ static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chun connection_assert_ref(c); pa_assert(chunk); - r = pa_memblockq_peek(c->input_memblockq, chunk); + if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0) { -/* pa_log("peeked %u %i", r >= 0 ? chunk->length: 0, r); */ + if (c->dead && pa_sink_input_safe_to_remove(i)) + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL); - if (c->dead && r < 0) - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL); + } else { + size_t old, new; - return r; -} - -/* Called from thread context */ -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { - connection *c; - size_t old, new; - - pa_assert(i); - c = CONNECTION(i->userdata); - connection_assert_ref(c); - pa_assert(length); + old = pa_memblockq_missing(c->input_memblockq); + pa_memblockq_drop(c->input_memblockq, chunk->length); + new = pa_memblockq_missing(c->input_memblockq); - old = pa_memblockq_missing(c->input_memblockq); - pa_memblockq_drop(c->input_memblockq, length); - new = pa_memblockq_missing(c->input_memblockq); - - if (new > old) { - if (pa_atomic_add(&c->playback.missing, new - old) <= 0) - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); + if (new > old) { + if (pa_atomic_add(&c->playback.missing, new - old) <= 0) + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); + } } + + return r; } /* Called from main context */ @@ -477,29 +468,38 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname)); - c->client->owner = p->module; + c->client->module = p->module; c->client->kill = client_kill_cb; c->client->userdata = c; if (p->mode & PLAYBACK) { pa_sink_input_new_data data; size_t l; + pa_sink *sink; + + if (!(sink = pa_namereg_get(c->protocol->core, c->protocol->sink_name, PA_NAMEREG_SINK, TRUE))) { + pa_log("Failed to get sink."); + goto fail; + } pa_sink_input_new_data_init(&data); data.driver = __FILE__; - data.name = c->client->name; - pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec); data.module = p->module; data.client = c->client; + data.sink = sink; + pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); + pa_sink_input_new_data_set_sample_spec(&data, &p->sample_spec); - if (!(c->sink_input = pa_sink_input_new(p->core, &data, 0))) { + c->sink_input = pa_sink_input_new(p->core, &data, 0); + pa_sink_input_new_data_done(&data); + + if (!c->sink_input) { pa_log("Failed to create sink input."); goto fail; } c->sink_input->parent.process_msg = sink_input_process_msg; - c->sink_input->peek = sink_input_peek_cb; - c->sink_input->drop = sink_input_drop_cb; + c->sink_input->pop = sink_input_pop_cb; c->sink_input->kill = sink_input_kill_cb; c->sink_input->userdata = c; @@ -511,6 +511,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) pa_frame_size(&p->sample_spec), (size_t) -1, l/PLAYBACK_BUFFER_FRAGMENTS, + 0, NULL); pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS; @@ -523,15 +524,25 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) if (p->mode & RECORD) { pa_source_output_new_data data; size_t l; + pa_source *source; + + if (!(source = pa_namereg_get(c->protocol->core, c->protocol->source_name, PA_NAMEREG_SOURCE, TRUE))) { + pa_log("Failed to get source."); + goto fail; + } pa_source_output_new_data_init(&data); data.driver = __FILE__; - data.name = c->client->name; - pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec); data.module = p->module; data.client = c->client; + data.source = source; + pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist); + pa_source_output_new_data_set_sample_spec(&data, &p->sample_spec); + + c->source_output = pa_source_output_new(p->core, &data, 0); + pa_source_output_new_data_done(&data); - if (!(c->source_output = pa_source_output_new(p->core, &data, 0))) { + if (!c->source_output) { pa_log("Failed to create source output."); goto fail; } @@ -548,6 +559,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) pa_frame_size(&p->sample_spec), 1, 0, + 0, NULL); pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c index 9d32a363..97ac2b08 100644 --- a/src/pulsecore/pstream.c +++ b/src/pulsecore/pstream.c @@ -374,7 +374,7 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa i = pa_xnew(struct item_info, 1); i->type = PA_PSTREAM_ITEM_MEMBLOCK; - n = MIN(length, bsm); + n = PA_MIN(length, bsm); i->chunk.index = chunk->index + idx; i->chunk.length = n; i->chunk.memblock = pa_memblock_ref(chunk->memblock); diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index fe7f1ad2..3d7e5364 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -47,7 +47,7 @@ #include "resampler.h" /* Number of samples of extra space we allow the resamplers to return */ -#define EXTRA_SAMPLES 128 +#define EXTRA_FRAMES 128 struct pa_resampler { pa_resample_method_t method; @@ -79,6 +79,15 @@ struct pa_resampler { unsigned i_counter; } trivial; + struct { /* data specific to the peak finder pseudo resampler */ + unsigned o_counter; + unsigned i_counter; + + float max_f[PA_CHANNELS_MAX]; + int16_t max_i[PA_CHANNELS_MAX]; + + } peaks; + #ifdef HAVE_LIBSAMPLERATE struct { /* data specific to libsamplerate */ SRC_STATE *state; @@ -99,6 +108,7 @@ static int copy_init(pa_resampler *r); static int trivial_init(pa_resampler*r); static int speex_init(pa_resampler*r); static int ffmpeg_init(pa_resampler*r); +static int peaks_init(pa_resampler*r); #ifdef HAVE_LIBSAMPLERATE static int libsamplerate_init(pa_resampler*r); #endif @@ -144,7 +154,8 @@ static int (* const init_table[])(pa_resampler*r) = { [PA_RESAMPLER_SPEEX_FIXED_BASE+10] = speex_init, [PA_RESAMPLER_FFMPEG] = ffmpeg_init, [PA_RESAMPLER_AUTO] = NULL, - [PA_RESAMPLER_COPY] = copy_init + [PA_RESAMPLER_COPY] = copy_init, + [PA_RESAMPLER_PEAKS] = peaks_init, }; static inline size_t sample_size(pa_sample_format_t f) { @@ -242,9 +253,9 @@ pa_resampler* pa_resampler_new( if ((method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) || (method == PA_RESAMPLER_FFMPEG)) r->work_format = PA_SAMPLE_S16NE; - else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY) { + else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY || method == PA_RESAMPLER_PEAKS) { - if (r->map_required || a->format != b->format) { + if (r->map_required || a->format != b->format || method == PA_RESAMPLER_PEAKS) { if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE || a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE || @@ -347,6 +358,12 @@ size_t pa_resampler_request(pa_resampler *r, size_t out_length) { return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz; } +size_t pa_resampler_result(pa_resampler *r, size_t in_length) { + pa_assert(r); + + return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz; +} + size_t pa_resampler_max_block_size(pa_resampler *r) { size_t block_size_max; pa_sample_spec ss; @@ -358,22 +375,17 @@ size_t pa_resampler_max_block_size(pa_resampler *r) { /* We deduce the "largest" sample spec we're using during the * conversion */ - ss = r->i_ss; - if (r->o_ss.channels > ss.channels) - ss.channels = r->o_ss.channels; + ss.channels = PA_MAX(r->i_ss.channels, r->o_ss.channels); /* We silently assume that the format enum is ordered by size */ - if (r->o_ss.format > ss.format) - ss.format = r->o_ss.format; - if (r->work_format > ss.format) - ss.format = r->work_format; + ss.format = PA_MAX(r->i_ss.format, r->o_ss.format); + ss.format = PA_MAX(ss.format, r->work_format); - if (r->o_ss.rate > ss.rate) - ss.rate = r->o_ss.rate; + ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate); fs = pa_frame_size(&ss); - return (((block_size_max/fs + EXTRA_SAMPLES)*r->i_ss.rate)/ss.rate)*r->i_fz; + return (((block_size_max/fs - EXTRA_FRAMES)*r->i_ss.rate)/ss.rate)*r->i_fz; } void pa_resampler_reset(pa_resampler *r) { @@ -420,7 +432,8 @@ static const char * const resample_methods[] = { "speex-fixed-10", "ffmpeg", "auto", - "copy" + "copy", + "peaks" }; const char *pa_resample_method_to_string(pa_resample_method_t m) { @@ -1069,7 +1082,7 @@ static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) { in_n_samples = input->length / r->w_sz; in_n_frames = in_n_samples / r->o_ss.channels; - out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_SAMPLES; + out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES; out_n_samples = out_n_frames * r->o_ss.channels; r->buf3.index = 0; @@ -1400,6 +1413,114 @@ static int trivial_init(pa_resampler*r) { return 0; } +/* Peak finder implementation */ + +static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { + size_t fz; + unsigned o_index; + void *src, *dst; + unsigned start = 0; + + pa_assert(r); + pa_assert(input); + pa_assert(output); + pa_assert(out_n_frames); + + fz = r->w_sz * r->o_ss.channels; + + src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; + dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index; + + for (o_index = 0;; o_index++, r->peaks.o_counter++) { + unsigned j; + + j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate); + j = j > r->peaks.i_counter ? j - r->peaks.i_counter : 0; + + if (j >= in_n_frames) + break; + + pa_assert(o_index * fz < pa_memblock_get_length(output->memblock)); + + + if (r->work_format == PA_SAMPLE_S16NE) { + unsigned i, c; + int16_t *s = (int16_t*) ((uint8_t*) src + fz * j); + int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index); + + for (i = start; i <= j; i++) + for (c = 0; c < r->o_ss.channels; c++, s++) { + int16_t n; + + n = *s < 0 ? -*s : *s; + + if (n > r->peaks.max_i[c]) + r->peaks.max_i[c] = n; + } + + for (c = 0; c < r->o_ss.channels; c++, d++) + *d = r->peaks.max_i[c]; + + memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i)); + } else { + unsigned i, c; + float *s = (float*) ((uint8_t*) src + fz * j); + float *d = (float*) ((uint8_t*) dst + fz * o_index); + + pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE); + + for (i = start; i <= j; i++) + for (c = 0; c < r->o_ss.channels; c++, s++) { + float n = fabsf(*s); + + if (n > r->peaks.max_f[c]) + r->peaks.max_f[c] = n; + } + + for (c = 0; c < r->o_ss.channels; c++, d++) + *d = r->peaks.max_f[c]; + + memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f)); + } + } + + pa_memblock_release(input->memblock); + pa_memblock_release(output->memblock); + + *out_n_frames = o_index; + + r->peaks.i_counter += in_n_frames; + + /* Normalize counters */ + while (r->peaks.i_counter >= r->i_ss.rate) { + pa_assert(r->peaks.o_counter >= r->o_ss.rate); + + r->peaks.i_counter -= r->i_ss.rate; + r->peaks.o_counter -= r->o_ss.rate; + } +} + +static void peaks_update_rates_or_reset(pa_resampler *r) { + pa_assert(r); + + r->peaks.i_counter = 0; + r->peaks.o_counter = 0; +} + +static int peaks_init(pa_resampler*r) { + pa_assert(r); + + r->peaks.o_counter = r->peaks.i_counter = 0; + memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i)); + memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f)); + + r->impl_resample = peaks_resample; + r->impl_update_rates = peaks_update_rates_or_reset; + r->impl_reset = peaks_update_rates_or_reset; + + return 0; +} + /*** ffmpeg based implementation ***/ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 82d01082..8534f5b5 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -46,6 +46,7 @@ typedef enum pa_resample_method { PA_RESAMPLER_FFMPEG, PA_RESAMPLER_AUTO, /* automatic select based on sample format */ PA_RESAMPLER_COPY, + PA_RESAMPLER_PEAKS, PA_RESAMPLER_MAX } pa_resample_method_t; @@ -69,6 +70,9 @@ void pa_resampler_free(pa_resampler *r); /* Returns the size of an input memory block which is required to return the specified amount of output data */ size_t pa_resampler_request(pa_resampler *r, size_t out_length); +/* Inverse of pa_resampler_request() */ +size_t pa_resampler_result(pa_resampler *r, size_t in_length); + /* Returns the maximum size of input blocks we can process without needing bounce buffers larger than the mempool tile size. */ size_t pa_resampler_max_block_size(pa_resampler *r); diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index 83008266..f7773be3 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -99,7 +99,7 @@ struct pa_rtpoll_item { PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree); -static void signal_handler_noop(int s) { } +static void signal_handler_noop(int s) { write(2, "signal\n", 7); } pa_rtpoll *pa_rtpoll_new(void) { pa_rtpoll *p; diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index 4ea5d446..4a532f29 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -43,26 +43,25 @@ #define PA_SILENCE_MAX (PA_PAGE_SIZE*16) pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length) { - size_t fs; + pa_memblock *b; + pa_assert(pool); pa_assert(spec); if (length <= 0) - length = pa_bytes_per_second(spec)/20; /* 50 ms */ + length = PA_MIN(pa_bytes_per_second(spec)/20, /* 50 ms */ + pa_mempool_block_size_max(pool)); if (length > PA_SILENCE_MAX) length = PA_SILENCE_MAX; - fs = pa_frame_size(spec); + length = pa_frame_align(length, spec); - length = (length+fs-1)/fs; - - if (length <= 0) - length = 1; + b = pa_silence_memblock(pa_memblock_new(pool, length), spec); - length *= fs; + pa_memblock_set_is_silence(b, TRUE); - return pa_silence_memblock(pa_memblock_new(pool, length), spec); + return b; } pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { @@ -74,6 +73,7 @@ pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { data = pa_memblock_acquire(b); pa_silence_memory(data, pa_memblock_get_length(b), spec); pa_memblock_release(b); + return b; } @@ -631,6 +631,9 @@ void pa_volume_memchunk( pa_assert(c->length % pa_frame_size(spec) == 0); pa_assert(volume); + if (pa_memblock_is_silence(c->memblock)) + return; + if (pa_cvolume_channels_equal_to(volume, PA_VOLUME_NORM)) return; diff --git a/src/pulsecore/shmasyncq.c b/src/pulsecore/shmasyncq.c new file mode 100644 index 00000000..7af2985c --- /dev/null +++ b/src/pulsecore/shmasyncq.c @@ -0,0 +1,222 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "fdsem.h" + +/* For debugging purposes we can define _Y to put and extra thread + * yield between each operation. */ + +/* #define PROFILE */ + +#ifdef PROFILE +#define _Y pa_thread_yield() +#else +#define _Y do { } while(0) +#endif + + +struct pa_shmasyncq { + pa_fdsem *read_fdsem, *write_fdsem; + pa_shmasyncq_data *data; +}; + +static int is_power_of_two(unsigned size) { + return !(size & (size - 1)); +} + +static int reduce(pa_shmasyncq *l, int value) { + return value & (unsigned) (l->n_elements - 1); +} + +static pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) { + pa_assert(i < l->data->n_elements); + + return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size))) +} + +static void *get_cell_data(pa_atomic_t *a) { + return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t)); +} + +pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]) { + pa_shmasyncq *l; + + pa_assert(n_elements > 0); + pa_assert(is_power_of_two(n_elements)); + pa_assert(element_size > 0); + pa_assert(data); + pa_assert(fd); + + l = pa_xnew(pa_shmasyncq, 1); + + l->data = data; + memset(data, 0, PA_SHMASYNCQ_SIZE(n_elements, element_size)); + + l->data->n_elements = n_elements; + l->data->element_size = element_size; + + if (!(l->read_fdsem = pa_fdsem_new_shm(&d->read_fdsem_data, &fd[0]))) { + pa_xfree(l); + return NULL; + } + + if (!(l->write_fdsem = pa_fdsem_new(&d->write_fdsem_data, &fd[1]))) { + pa_fdsem_free(l->read_fdsem); + pa_xfree(l); + return NULL; + } + + return l; +} + +void pa_shmasyncq_free(pa_shmasyncq *l, pa_free_cb_t free_cb) { + pa_assert(l); + + if (free_cb) { + void *p; + + while ((p = pa_shmasyncq_pop(l, 0))) + free_cb(p); + } + + pa_fdsem_free(l->read_fdsem); + pa_fdsem_free(l->write_fdsem); + pa_xfree(l); +} + +int pa_shmasyncq_push(pa_shmasyncq*l, void *p, int wait) { + int idx; + pa_atomic_ptr_t *cells; + + pa_assert(l); + pa_assert(p); + + cells = PA_SHMASYNCQ_CELLS(l); + + _Y; + idx = reduce(l, l->write_idx); + + if (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)) { + + if (!wait) + return -1; + +/* pa_log("sleeping on push"); */ + + do { + pa_fdsem_wait(l->read_fdsem); + } while (!pa_atomic_ptr_cmpxchg(&cells[idx], NULL, p)); + } + + _Y; + l->write_idx++; + + pa_fdsem_post(l->write_fdsem); + + return 0; +} + +void* pa_shmasyncq_pop(pa_shmasyncq*l, int wait) { + int idx; + void *ret; + pa_atomic_ptr_t *cells; + + pa_assert(l); + + cells = PA_SHMASYNCQ_CELLS(l); + + _Y; + idx = reduce(l, l->read_idx); + + if (!(ret = pa_atomic_ptr_load(&cells[idx]))) { + + if (!wait) + return NULL; + +/* pa_log("sleeping on pop"); */ + + do { + pa_fdsem_wait(l->write_fdsem); + } while (!(ret = pa_atomic_ptr_load(&cells[idx]))); + } + + pa_assert(ret); + + /* Guaranteed to succeed if we only have a single reader */ + pa_assert_se(pa_atomic_ptr_cmpxchg(&cells[idx], ret, NULL)); + + _Y; + l->read_idx++; + + pa_fdsem_post(l->read_fdsem); + + return ret; +} + +int pa_shmasyncq_get_fd(pa_shmasyncq *q) { + pa_assert(q); + + return pa_fdsem_get(q->write_fdsem); +} + +int pa_shmasyncq_before_poll(pa_shmasyncq *l) { + int idx; + pa_atomic_ptr_t *cells; + + pa_assert(l); + + cells = PA_SHMASYNCQ_CELLS(l); + + _Y; + idx = reduce(l, l->read_idx); + + for (;;) { + if (pa_atomic_ptr_load(&cells[idx])) + return -1; + + if (pa_fdsem_before_poll(l->write_fdsem) >= 0) + return 0; + } + + return 0; +} + +void pa_shmasyncq_after_poll(pa_shmasyncq *l) { + pa_assert(l); + + pa_fdsem_after_poll(l->write_fdsem); +} diff --git a/src/pulsecore/shmasyncq.h b/src/pulsecore/shmasyncq.h new file mode 100644 index 00000000..ca35ffd2 --- /dev/null +++ b/src/pulsecore/shmasyncq.h @@ -0,0 +1,62 @@ +#ifndef foopulseshmasyncqhfoo +#define foopulseshmasyncqhfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include + +#include +#include + +/* Similar to pa_asyncq, but stores data in a shared memory segment */ + + +struct pa_shmasyncq_data { + unsigned n_elements; + size_t element_size; + unsigned read_idx; + unsigned write_idx; + pa_fdsem_data read_fdsem_data, write_fdsem_data; +}; + +#define PA_SHMASYNCQ_DEFAULT_N_ELEMENTS 128 +#define PA_SHMASYNCQ_SIZE(n_elements, element_size) (PA_ALIGN(sizeof(pa_shmasyncq_data)) + (((n_elements) * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size))))) +#define PA_SHMASYNCQ_DEFAULT_SIZE(element_size) PA_SHMASYNCQ_SIZE(PA_SHMASYNCQ_DEFAULT_N_ELEMENTS, element_size) + +typedef struct pa_shmasyncq pa_shmasyncq; + +pa_shmasyncq *pa_shmasyncq_new(unsigned n_elements, size_t element_size, void *data, int fd[2]); +void pa_shmasyncq_free(pa_shmasyncq* q, pa_free_cb_t free_cb); + +void* pa_shmasyncq_pop_begin(pa_shmasyncq *q, pa_bool_t wait); +void pa_shmasyncq_pop_commit(pa_shmasyncq *q); + +int* pa_shmasyncq_push_begin(pa_shmasyncq *q, pa_bool_t wait); +void pa_shmasyncq_push_commit(pa_shmasyncq *q); + +int pa_shmasyncq_get_fd(pa_shmasyncq *q); +int pa_shmasyncq_before_poll(pa_shmasyncq *a); +void pa_shmasyncq_after_poll(pa_shmasyncq *a); + +#endif diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 07ddb83a..e15aa7bb 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -41,8 +41,8 @@ #include "sink-input.h" +#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) #define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE) -#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12) #define MOVE_BUFFER_LENGTH (PA_PAGE_SIZE*256) static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject); @@ -54,10 +54,18 @@ pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data memset(data, 0, sizeof(*data)); data->resample_method = PA_RESAMPLER_INVALID; + data->proplist = pa_proplist_new(); return data; } +void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) { + pa_assert(data); + + if ((data->sample_spec_is_set = !!spec)) + data->sample_spec = *spec; +} + void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map) { pa_assert(data); @@ -72,18 +80,17 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv data->volume = *volume; } -void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec) { +void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { pa_assert(data); - if ((data->sample_spec_is_set = !!spec)) - data->sample_spec = *spec; + data->muted_is_set = TRUE; + data->muted = !!mute; } -void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { +void pa_sink_input_new_data_done(pa_sink_input_new_data *data) { pa_assert(data); - data->muted_is_set = TRUE; - data->muted = !!mute; + pa_proplist_free(data->proplist); } pa_sink_input* pa_sink_input_new( @@ -94,6 +101,7 @@ pa_sink_input* pa_sink_input_new( pa_sink_input *i; pa_resampler *resampler = NULL; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + pa_memblock *silence; pa_assert(core); pa_assert(data); @@ -102,7 +110,6 @@ pa_sink_input* pa_sink_input_new( return NULL; pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); - pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name)); if (!data->sink) data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, 1); @@ -132,6 +139,9 @@ pa_sink_input* pa_sink_input_new( pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels); + if (!data->muted_is_set) + data->muted = FALSE; + if (flags & PA_SINK_INPUT_FIX_FORMAT) data->sample_spec.format = data->sink->sample_spec.format; @@ -150,9 +160,6 @@ pa_sink_input* pa_sink_input_new( if (data->volume.channels != data->sample_spec.channels) pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume)); - if (!data->muted_is_set) - data->muted = 0; - if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; @@ -192,7 +199,7 @@ pa_sink_input* pa_sink_input_new( i->core = core; i->state = PA_SINK_INPUT_INIT; i->flags = flags; - i->name = pa_xstrdup(data->name); + i->proplist = pa_proplist_copy(data->proplist); i->driver = pa_xstrdup(data->driver); i->module = data->module; i->sink = data->sink; @@ -215,8 +222,9 @@ pa_sink_input* pa_sink_input_new( } else i->sync_next = i->sync_prev = NULL; - i->peek = NULL; - i->drop = NULL; + i->pop = NULL; + i->rewind = NULL; + i->set_max_rewind = NULL; i->kill = NULL; i->get_latency = NULL; i->attach = NULL; @@ -226,22 +234,37 @@ pa_sink_input* pa_sink_input_new( i->userdata = NULL; i->thread_info.state = i->state; + i->thread_info.attached = FALSE; pa_atomic_store(&i->thread_info.drained, 1); + pa_atomic_store(&i->thread_info.render_memblockq_is_empty, 0); i->thread_info.sample_spec = i->sample_spec; - i->thread_info.silence_memblock = NULL; - i->thread_info.move_silence = 0; - pa_memchunk_reset(&i->thread_info.resampled_chunk); i->thread_info.resampler = resampler; i->thread_info.volume = i->volume; i->thread_info.muted = i->muted; - i->thread_info.attached = FALSE; + i->thread_info.requested_sink_latency = 0; + i->thread_info.rewrite_nbytes = 0; + i->thread_info.ignore_rewind = FALSE; + + silence = pa_silence_memblock_new(i->sink->core->mempool, &i->sink->sample_spec, 0); + + i->thread_info.render_memblockq = pa_memblockq_new( + 0, + MEMBLOCKQ_MAXLENGTH, + 0, + pa_frame_size(&i->sink->sample_spec), + 0, + 1, + 0, + silence); + + pa_memblock_unref(silence); pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0); pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0); pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s", i->index, - i->name, + pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), i->sink->name, pa_sample_spec_snprint(st, sizeof(st), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map)); @@ -325,8 +348,9 @@ void pa_sink_input_unlink(pa_sink_input *i) { } else i->state = PA_SINK_INPUT_UNLINKED; - i->peek = NULL; - i->drop = NULL; + i->pop = NULL; + i->rewind = NULL; + i->set_max_rewind = NULL; i->kill = NULL; i->get_latency = NULL; i->attach = NULL; @@ -352,20 +376,19 @@ static void sink_input_free(pa_object *o) { if (PA_SINK_INPUT_LINKED(i->state)) pa_sink_input_unlink(i); - pa_log_info("Freeing output %u \"%s\"", i->index, i->name); + pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME))); pa_assert(!i->thread_info.attached); - if (i->thread_info.resampled_chunk.memblock) - pa_memblock_unref(i->thread_info.resampled_chunk.memblock); + if (i->thread_info.render_memblockq) + pa_memblockq_free(i->thread_info.render_memblockq); if (i->thread_info.resampler) pa_resampler_free(i->thread_info.resampler); - if (i->thread_info.silence_memblock) - pa_memblock_unref(i->thread_info.silence_memblock); + if (i->proplist) + pa_proplist_free(i->proplist); - pa_xfree(i->name); pa_xfree(i->driver); pa_xfree(i); } @@ -374,8 +397,8 @@ void pa_sink_input_put(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert(i->state == PA_SINK_INPUT_INIT); - pa_assert(i->peek); - pa_assert(i->drop); + pa_assert(i->pop); + pa_assert(i->rewind); i->thread_info.state = i->state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; i->thread_info.volume = i->volume; @@ -384,8 +407,8 @@ void pa_sink_input_put(pa_sink_input *i) { if (i->state == PA_SINK_INPUT_CORKED) i->sink->n_corked++; - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); pa_sink_update_status(i->sink); + pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); @@ -419,85 +442,83 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) { } /* Called from thread context */ -int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume) { - int ret = -1; - int do_volume_adj_here; - int volume_is_norm; - size_t block_size_max; +int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) { + pa_bool_t do_volume_adj_here; + pa_bool_t volume_is_norm; + size_t block_size_max_sink, block_size_max_sink_input; + size_t ilength; pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); - pa_assert(pa_frame_aligned(length, &i->sink->sample_spec)); + pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec)); pa_assert(chunk); pa_assert(volume); - if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED) - goto finish; + pa_log_debug("peek"); + + if (!i->pop || i->thread_info.state == PA_SINK_INPUT_CORKED) + return -1; pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING || i->thread_info.state == PA_SINK_INPUT_DRAINED); + /* If there's still some rewrite request the handle, but the sink + didn't do this for us, we do it here. However, since the sink + apparently doesn't support rewinding, we pass 0 here. This still + allows rewinding through the render buffer. */ + pa_sink_input_rewind(i, 0); + + block_size_max_sink_input = i->thread_info.resampler ? + pa_resampler_max_block_size(i->thread_info.resampler) : + pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec); + + block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec); + /* Default buffer size */ - if (length <= 0) - length = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec); - - /* Make sure the buffer fits in the mempool tile */ - block_size_max = pa_mempool_block_size_max(i->sink->core->mempool); - if (length > block_size_max) - length = pa_frame_align(block_size_max, &i->sink->sample_spec); - - if (i->thread_info.move_silence > 0) { - size_t l; - - /* We have just been moved and shall play some silence for a - * while until the old sink has drained its playback buffer */ - - if (!i->thread_info.silence_memblock) - i->thread_info.silence_memblock = pa_silence_memblock_new( - i->sink->core->mempool, - &i->sink->sample_spec, - pa_frame_align(SILENCE_BUFFER_LENGTH, &i->sink->sample_spec)); - - chunk->memblock = pa_memblock_ref(i->thread_info.silence_memblock); - chunk->index = 0; - l = pa_memblock_get_length(chunk->memblock); - chunk->length = i->thread_info.move_silence < l ? i->thread_info.move_silence : l; - - ret = 0; - do_volume_adj_here = 1; - goto finish; - } + if (slength <= 0) + slength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sink->sample_spec); - if (!i->thread_info.resampler) { - do_volume_adj_here = 0; /* FIXME??? */ - ret = i->peek(i, length, chunk); - goto finish; - } + if (slength > block_size_max_sink) + slength = block_size_max_sink; + + if (i->thread_info.resampler) { + ilength = pa_resampler_request(i->thread_info.resampler, slength); + + if (ilength <= 0) + ilength = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec); + } else + ilength = slength; + + if (ilength > block_size_max_sink_input) + ilength = block_size_max_sink_input; + + /* If the channel maps of the sink and this stream differ, we need + * to adjust the volume *before* we resample. Otherwise we can do + * it after and leave it for the sink code */ do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map); volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted; - while (!i->thread_info.resampled_chunk.memblock) { + while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) { pa_memchunk tchunk; - size_t l, rmbs; - l = pa_resampler_request(i->thread_info.resampler, length); + /* There's nothing in our render queue. We need to fill it up + * with data from the implementor. */ - if (l <= 0) - l = pa_frame_align(CONVERT_BUFFER_LENGTH, &i->sample_spec); + if (i->pop(i, ilength, &tchunk) < 0) { + pa_atomic_store(&i->thread_info.drained, 1); - rmbs = pa_resampler_max_block_size(i->thread_info.resampler); - if (l > rmbs) - l = rmbs; + /* OK, we got no data from the implementor, so let's just skip ahead */ + pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ); + break; + } - if ((ret = i->peek(i, l, &tchunk)) < 0) - goto finish; + pa_atomic_store(&i->thread_info.drained, 0); pa_assert(tchunk.length > 0); + pa_assert(tchunk.memblock); - if (tchunk.length > l) - tchunk.length = l; - - i->drop(i, tchunk.length); + if (tchunk.length > block_size_max_sink_input) + tchunk.length = block_size_max_sink_input; /* It might be necessary to adjust the volume here */ if (do_volume_adj_here && !volume_is_norm) { @@ -509,137 +530,146 @@ int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_c pa_volume_memchunk(&tchunk, &i->thread_info.sample_spec, &i->thread_info.volume); } - pa_resampler_run(i->thread_info.resampler, &tchunk, &i->thread_info.resampled_chunk); + if (!i->thread_info.resampler) + pa_memblockq_push_align(i->thread_info.render_memblockq, &tchunk); + else { + pa_memchunk rchunk; + pa_resampler_run(i->thread_info.resampler, &tchunk, &rchunk); + + if (rchunk.memblock) { + pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk); + pa_memblock_unref(rchunk.memblock); + } + } + pa_memblock_unref(tchunk.memblock); } - pa_assert(i->thread_info.resampled_chunk.memblock); - pa_assert(i->thread_info.resampled_chunk.length > 0); + pa_assert_se(pa_memblockq_peek(i->thread_info.render_memblockq, chunk) >= 0); - *chunk = i->thread_info.resampled_chunk; - pa_memblock_ref(i->thread_info.resampled_chunk.memblock); + pa_assert(chunk->length > 0); + pa_assert(chunk->memblock); - ret = 0; + if (chunk->length > block_size_max_sink) + chunk->length = block_size_max_sink; -finish: + /* Let's see if we had to apply the volume adjustment ourselves, + * or if this can be done by the sink for us */ - if (ret >= 0) - pa_atomic_store(&i->thread_info.drained, 0); - else if (ret < 0) - pa_atomic_store(&i->thread_info.drained, 1); - - if (ret >= 0) { - /* Let's see if we had to apply the volume adjustment - * ourselves, or if this can be done by the sink for us */ - - if (do_volume_adj_here) - /* We had different channel maps, so we already did the adjustment */ - pa_cvolume_reset(volume, i->sink->sample_spec.channels); - else if (i->thread_info.muted) - /* We've both the same channel map, so let's have the sink do the adjustment for us*/ - pa_cvolume_mute(volume, i->sink->sample_spec.channels); - else - *volume = i->thread_info.volume; - } + if (do_volume_adj_here) + /* We had different channel maps, so we already did the adjustment */ + pa_cvolume_reset(volume, i->sink->sample_spec.channels); + else if (i->thread_info.muted) + /* We've both the same channel map, so let's have the sink do the adjustment for us*/ + pa_cvolume_mute(volume, i->sink->sample_spec.channels); + else + *volume = i->thread_info.volume; + + pa_atomic_store(&i->thread_info.render_memblockq_is_empty, pa_memblockq_is_empty(i->thread_info.render_memblockq)); - return ret; + return 0; } /* Called from thread context */ -void pa_sink_input_drop(pa_sink_input *i, size_t length) { +void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); - pa_assert(pa_frame_aligned(length, &i->sink->sample_spec)); - pa_assert(length > 0); + pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); + pa_assert(nbytes > 0); - if (!i->peek || !i->drop || i->thread_info.state == PA_SINK_INPUT_CORKED) + if (i->thread_info.state == PA_SINK_INPUT_CORKED) return; - if (i->thread_info.move_silence > 0) { + /* If there's still some rewrite request the handle, but the sink + didn't do this for us, we do it here. However, since the sink + apparently doesn't support rewinding, we pass 0 here. This still + allows rewinding through the render buffer. */ + if (i->thread_info.rewrite_nbytes > 0) + pa_sink_input_rewind(i, 0); - if (i->thread_info.move_silence >= length) { - i->thread_info.move_silence -= length; - length = 0; - } else { - length -= i->thread_info.move_silence; - i->thread_info.move_silence = 0; - } - - if (i->thread_info.move_silence <= 0) { - if (i->thread_info.silence_memblock) { - pa_memblock_unref(i->thread_info.silence_memblock); - i->thread_info.silence_memblock = NULL; - } - } + pa_memblockq_drop(i->thread_info.render_memblockq, nbytes); - if (length <= 0) - return; - } + pa_atomic_store(&i->thread_info.render_memblockq_is_empty, pa_memblockq_is_empty(i->thread_info.render_memblockq)); +} - if (i->thread_info.resampled_chunk.memblock) { - size_t l = length; +/* Called from thread context */ +void pa_sink_input_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { + pa_sink_input_assert_ref(i); - if (l > i->thread_info.resampled_chunk.length) - l = i->thread_info.resampled_chunk.length; + pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); - i->thread_info.resampled_chunk.index += l; - i->thread_info.resampled_chunk.length -= l; + pa_log_debug("rewind(%u, %u)", nbytes, i->thread_info.rewrite_nbytes); - if (i->thread_info.resampled_chunk.length <= 0) { - pa_memblock_unref(i->thread_info.resampled_chunk.memblock); - pa_memchunk_reset(&i->thread_info.resampled_chunk); - } + if (i->thread_info.state == PA_SINK_INPUT_CORKED) + return; - length -= l; + if (i->thread_info.ignore_rewind) { + i->thread_info.rewrite_nbytes = 0; + i->thread_info.ignore_rewind = FALSE; + return; } - if (length > 0) { + if (nbytes > 0) + pa_log_debug("Have to rewind %u bytes.", nbytes); - if (i->thread_info.resampler) { - /* So, we have a resampler. To avoid discontinuities we - * have to actually read all data that could be read and - * pass it through the resampler. */ + if (i->thread_info.rewrite_nbytes > 0) { + size_t max_rewrite; - while (length > 0) { - pa_memchunk chunk; - pa_cvolume volume; + /* Calculate how much make sense to rewrite at most */ + if ((max_rewrite = nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq)) > 0) { + size_t amount, r; - if (pa_sink_input_peek(i, length, &chunk, &volume) >= 0) { - size_t l; + /* Transform into local domain */ + if (i->thread_info.resampler) + max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite); - pa_memblock_unref(chunk.memblock); + /* Calculate how much of the rewinded data should actually be rewritten */ + amount = PA_MIN(max_rewrite, i->thread_info.rewrite_nbytes); - l = chunk.length; - if (l > length) - l = length; + /* Convert back to to sink domain */ + r = i->thread_info.resampler ? pa_resampler_result(i->thread_info.resampler, amount) : amount; - pa_sink_input_drop(i, l); - length -= l; + /* Ok, now update the write pointer */ + pa_memblockq_seek(i->thread_info.render_memblockq, -r, PA_SEEK_RELATIVE); - } else { - size_t l; + /* Tell the implementor */ + if (i->rewind) + i->rewind(i, amount); - l = pa_resampler_request(i->thread_info.resampler, length); + /* And reset the resampler */ + if (i->thread_info.resampler) + pa_resampler_reset(i->thread_info.resampler); + } - /* Hmmm, peeking failed, so let's at least drop - * the right amount of data */ - if (l > 0) - if (i->drop) - i->drop(i, l); + i->thread_info.rewrite_nbytes = 0; + } - break; - } - } + if (nbytes > 0) + pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); +} - } else { +/* Called from thread context */ +void pa_sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) { + pa_sink_input_assert_ref(i); - /* We have no resampler, hence let's just drop the data */ + pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); - if (i->drop) - i->drop(i, length); - } - } + pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes); + + if (i->set_max_rewind) + i->set_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes); +} + +void pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { + pa_sink_input_assert_ref(i); + pa_assert(PA_SINK_INPUT_LINKED(i->state)); + + pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); } void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { @@ -707,19 +737,24 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { } void pa_sink_input_set_name(pa_sink_input *i, const char *name) { + const char *old; pa_sink_input_assert_ref(i); - if (!i->name && !name) + if (!name && !pa_proplist_contains(i->proplist, PA_PROP_MEDIA_NAME)) return; - if (i->name && name && !strcmp(i->name, name)) + old = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME); + + if (old && name && !strcmp(old, name)) return; - pa_xfree(i->name); - i->name = pa_xstrdup(name); + if (name) + pa_proplist_sets(i->proplist, PA_PROP_MEDIA_NAME, name); + else + pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME); if (PA_SINK_INPUT_LINKED(i->state)) { - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_NAME_CHANGED], i); + pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } } @@ -829,20 +864,29 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { /* Okey, let's move it */ if (info.buffer_bytes > 0) { + pa_proplist *p; + + p = pa_proplist_new(); + pa_proplist_sets(p, PA_PROP_MEDIA_NAME, "Ghost For Moved Stream"); + pa_proplist_sets(p, PA_PROP_MEDIA_ROLE, "routing"); info.ghost_sink_input = pa_memblockq_sink_input_new( origin, - "Ghost Stream", &origin->sample_spec, &origin->channel_map, NULL, - NULL); + NULL, + p); + + pa_proplist_free(p); - info.ghost_sink_input->thread_info.state = info.ghost_sink_input->state = PA_SINK_INPUT_RUNNING; - info.ghost_sink_input->thread_info.volume = info.ghost_sink_input->volume; - info.ghost_sink_input->thread_info.muted = info.ghost_sink_input->muted; + if (info.ghost_sink_input) { + info.ghost_sink_input->thread_info.state = info.ghost_sink_input->state = PA_SINK_INPUT_RUNNING; + info.ghost_sink_input->thread_info.volume = info.ghost_sink_input->volume; + info.ghost_sink_input->thread_info.muted = info.ghost_sink_input->muted; - info.buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, NULL); + info.buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, 0, NULL); + } } } @@ -867,34 +911,26 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { /* Replace resampler */ if (new_resampler != i->thread_info.resampler) { + pa_memblock *silence; + if (i->thread_info.resampler) pa_resampler_free(i->thread_info.resampler); i->thread_info.resampler = new_resampler; /* if the resampler changed, the silence memblock is * probably invalid now, too */ - if (i->thread_info.silence_memblock) { - pa_memblock_unref(i->thread_info.silence_memblock); - i->thread_info.silence_memblock = NULL; - } - } - /* Dump already resampled data */ - if (i->thread_info.resampled_chunk.memblock) { - /* Hmm, this data has already been added to the ghost queue, presumably, hence let's sleep a little bit longer */ - silence_usec += pa_bytes_to_usec(i->thread_info.resampled_chunk.length, &origin->sample_spec); - pa_memblock_unref(i->thread_info.resampled_chunk.memblock); - pa_memchunk_reset(&i->thread_info.resampled_chunk); + silence = pa_silence_memblock_new(i->sink->core->mempool, &dest->sample_spec, new_resampler ? pa_resampler_max_block_size(new_resampler) : 0); + pa_memblockq_set_silence(i->thread_info.render_memblockq, silence); + pa_memblock_unref(silence); + } + pa_memblockq_flush(i->thread_info.render_memblockq); + /* Calculate the new sleeping time */ - if (immediately) - i->thread_info.move_silence = 0; - else - i->thread_info.move_silence = pa_usec_to_bytes( - pa_bytes_to_usec(i->thread_info.move_silence, &origin->sample_spec) + - silence_usec, - &dest->sample_spec); + if (!immediately) + pa_memblockq_seek(i->thread_info.render_memblockq, pa_usec_to_bytes(silence_usec, &dest->sample_spec), PA_SEEK_RELATIVE); pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); @@ -924,20 +960,18 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t switch (code) { case PA_SINK_INPUT_MESSAGE_SET_VOLUME: i->thread_info.volume = *((pa_cvolume*) userdata); + pa_sink_input_request_rewrite(i, 0); return 0; case PA_SINK_INPUT_MESSAGE_SET_MUTE: i->thread_info.muted = PA_PTR_TO_UINT(userdata); + pa_sink_input_request_rewrite(i, 0); return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = userdata; - if (i->thread_info.resampled_chunk.memblock) - *r += pa_bytes_to_usec(i->thread_info.resampled_chunk.length, &i->sink->sample_spec); - - if (i->thread_info.move_silence) - *r += pa_bytes_to_usec(i->thread_info.move_silence, &i->sink->sample_spec); + *r += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec); return 0; } @@ -974,6 +1008,13 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t return 0; } + + case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: + + i->thread_info.requested_sink_latency = (pa_usec_t) offset; + pa_sink_invalidate_requested_latency(i->sink); + + return 0; } return -1; @@ -987,3 +1028,38 @@ pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) { return i->state; } + +pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { + pa_sink_input_assert_ref(i); + + if (i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED || i->state == PA_SINK_INPUT_CORKED) + return pa_atomic_load(&i->thread_info.render_memblockq_is_empty); + + return TRUE; +} + +void pa_sink_input_request_rewrite(pa_sink_input *i, size_t nbytes /* in our sample spec */) { + size_t l, lbq; + + pa_sink_input_assert_ref(i); + + lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); + + if (nbytes <= 0) { + nbytes = + i->thread_info.resampler ? + pa_resampler_request(i->thread_info.resampler, i->sink->thread_info.max_rewind + lbq) : + (i->sink->thread_info.max_rewind + lbq); + } + + i->thread_info.rewrite_nbytes = PA_MAX(nbytes, i->thread_info.rewrite_nbytes); + + /* Transform to sink domain */ + l = i->thread_info.resampler ? pa_resampler_result(i->thread_info.resampler, nbytes) : nbytes; + + if (l <= 0) + return; + + if (l > lbq) + pa_sink_request_rewind(i->sink, l - lbq); +} diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 8975db9e..c74b8912 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -73,7 +73,8 @@ struct pa_sink_input { pa_sink_input_state_t state; pa_sink_input_flags_t flags; - char *name, *driver; /* may be NULL */ + pa_proplist *proplist; + char *driver; /* may be NULL */ pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ @@ -87,17 +88,26 @@ struct pa_sink_input { pa_cvolume volume; pa_bool_t muted; - /* Returns the chunk of audio data (but doesn't drop it - * yet!). Returns -1 on failure. Called from IO thread context. If - * data needs to be generated from scratch then please in the - * specified length. This is an optimization only. If less data is - * available, it's fine to return a smaller block. If more data is - * already ready, it is better to return the full block.*/ - int (*peek) (pa_sink_input *i, size_t length, pa_memchunk *chunk); + pa_resample_method_t resample_method; - /* Drops the specified number of bytes, usually called right after - * peek(), but not necessarily. Called from IO thread context. */ - void (*drop) (pa_sink_input *i, size_t length); + /* Returns the chunk of audio data and drops it from the + * queue. Returns -1 on failure. Called from IO thread context. If + * data needs to be generated from scratch then please in the + * specified length request_nbytes. This is an optimization + * only. If less data is available, it's fine to return a smaller + * block. If more data is already ready, it is better to return + * the full block. */ + int (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); + + /* Rewind the queue by the specified number of bytes. Called just + * before peek() if it is called at all. Only called if the sink + * input driver ever plans to call + * pa_sink_input_request_rewrite(). Called from IO context. */ + void (*rewind) (pa_sink_input *i, size_t nbytes); + + /* Called whenever the maximum rewindable size of the sink + * changes. Called from UI context. */ + void (*set_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ /* If non-NULL this function is called when the input is first * connected to a sink or when the rtpoll/asyncmsgq fields @@ -128,29 +138,28 @@ struct pa_sink_input { instead. */ pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ - pa_resample_method_t resample_method; - struct { pa_sink_input_state_t state; - pa_atomic_t drained; + pa_atomic_t drained, render_memblockq_is_empty; pa_bool_t attached; /* True only between ->attach() and ->detach() calls */ pa_sample_spec sample_spec; - pa_memchunk resampled_chunk; pa_resampler *resampler; /* may be NULL */ - /* Some silence to play before the actual data. This is used to - * compensate for latency differences when moving a sink input - * "hot" between sinks. */ - size_t move_silence; - pa_memblock *silence_memblock; /* may be NULL */ + /* We maintain a history of resampled audio data here. */ + pa_memblockq *render_memblockq; + size_t rewrite_nbytes; + pa_bool_t ignore_rewind; pa_sink_input *sync_prev, *sync_next; pa_cvolume volume; pa_bool_t muted; + + /* The requested latency for the sink */ + pa_usec_t requested_sink_latency; } thread_info; void *userdata; @@ -165,11 +174,14 @@ enum { PA_SINK_INPUT_MESSAGE_GET_LATENCY, PA_SINK_INPUT_MESSAGE_SET_RATE, PA_SINK_INPUT_MESSAGE_SET_STATE, + PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, PA_SINK_INPUT_MESSAGE_MAX }; typedef struct pa_sink_input_new_data { - const char *name, *driver; + pa_proplist *proplist; + + const char *driver; pa_module *module; pa_client *client; @@ -190,16 +202,17 @@ typedef struct pa_sink_input_new_data { pa_sink_input *sync_base; } pa_sink_input_new_data; -typedef struct pa_sink_input_move_hook_data { - pa_sink_input *sink_input; - pa_sink *destination; -} pa_sink_input_move_hook_data; - pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute); +void pa_sink_input_new_data_done(pa_sink_input_new_data *data); + +typedef struct pa_sink_input_move_hook_data { + pa_sink_input *sink_input; + pa_sink *destination; +} pa_sink_input_move_hook_data; /* To be called by the implementing module only */ @@ -213,7 +226,19 @@ void pa_sink_input_unlink(pa_sink_input* i); void pa_sink_input_set_name(pa_sink_input *i, const char *name); -/* Callable by everyone */ +void pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec); + +/* Request that the specified number of bytes already written out to +the hw device is rewritten, if possible. If this function is used you +need to supply the ->rewind() function pointer. Please note that this +is only a kind request. The sink driver may not be able to fulfill it +fully -- or at all. If the request for a rewrite was successful, the +sink driver will call ->rewind() and pass the number of bytes that +could be rewound in the HW device. This functionality is required for +implementing the "zero latency" write-through functionality. */ +void pa_sink_input_request_rewrite(pa_sink_input *i, size_t nbytes); + +/* Callable by everyone from main thread*/ /* External code may request disconnection with this function */ void pa_sink_input_kill(pa_sink_input*i); @@ -235,10 +260,14 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately); pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i); -/* To be used exclusively by the sink driver thread */ +pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i); +/* To be used exclusively by the sink driver IO thread */ int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume); void pa_sink_input_drop(pa_sink_input *i, size_t length); +void pa_sink_input_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); +void pa_sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); + int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); typedef struct pa_sink_input_move_info { @@ -248,4 +277,5 @@ typedef struct pa_sink_input_move_info { size_t buffer_bytes; } pa_sink_input_move_info; + #endif diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 9adb6097..cded6ba7 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -47,42 +47,105 @@ #define MAX_MIX_CHANNELS 32 #define MIX_BUFFER_LENGTH (PA_PAGE_SIZE) -#define SILENCE_BUFFER_LENGTH (PA_PAGE_SIZE*12) static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject); static void sink_free(pa_object *s); +pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) { + pa_assert(data); + + memset(data, 0, sizeof(*data)); + data->proplist = pa_proplist_new(); + + return data; +} + +void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name) { + pa_assert(data); + + pa_xfree(data->name); + data->name = pa_xstrdup(name); +} + +void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec) { + pa_assert(data); + + if ((data->sample_spec_is_set = !!spec)) + data->sample_spec = *spec; +} + +void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map) { + pa_assert(data); + + if ((data->channel_map_is_set = !!map)) + data->channel_map = *map; +} + +void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) { + pa_assert(data); + + if ((data->volume_is_set = !!volume)) + data->volume = *volume; +} + +void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) { + pa_assert(data); + + data->muted_is_set = TRUE; + data->muted = !!mute; +} + +void pa_sink_new_data_done(pa_sink_new_data *data) { + pa_assert(data); + + pa_xfree(data->name); + pa_proplist_free(data->proplist); +} + pa_sink* pa_sink_new( pa_core *core, - const char *driver, - const char *name, - int fail, - const pa_sample_spec *spec, - const pa_channel_map *map) { + pa_sink_new_data *data, + pa_sink_flags_t flags) { pa_sink *s; - char *n = NULL; - char st[256]; - pa_channel_map tmap; + char *d; + const char *name; + char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + pa_source_new_data source_data; pa_assert(core); - pa_assert(name); - pa_assert(spec); + pa_assert(data); + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) + return NULL; + + pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); + pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); + + pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec)); + + if (!data->channel_map_is_set) + pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT)); + + pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); + pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); - pa_return_null_if_fail(pa_sample_spec_valid(spec)); + if (!data->volume_is_set) + pa_cvolume_reset(&data->volume, data->sample_spec.channels); - if (!map) - pa_return_null_if_fail((map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT))); + pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); + pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels); - pa_return_null_if_fail(map && pa_channel_map_valid(map)); - pa_return_null_if_fail(map->channels == spec->channels); - pa_return_null_if_fail(!driver || pa_utf8_valid(driver)); - pa_return_null_if_fail(name && pa_utf8_valid(name) && *name); + if (!data->muted_is_set) + data->muted = FALSE; + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) + return NULL; s = pa_msgobject_new(pa_sink); - if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SINK, s, fail))) { + if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) { pa_xfree(s); return NULL; } @@ -92,20 +155,20 @@ pa_sink* pa_sink_new( s->core = core; s->state = PA_SINK_INIT; - s->flags = 0; + s->flags = flags; s->name = pa_xstrdup(name); - s->description = NULL; - s->driver = pa_xstrdup(driver); - s->module = NULL; + s->proplist = pa_proplist_copy(data->proplist); + s->driver = pa_xstrdup(data->driver); + s->module = data->module; - s->sample_spec = *spec; - s->channel_map = *map; + s->sample_spec = data->sample_spec; + s->channel_map = data->channel_map; s->inputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; - pa_cvolume_reset(&s->volume, spec->channels); - s->muted = FALSE; + s->volume = data->volume; + s->muted = data->muted; s->refresh_volume = s->refresh_mute = FALSE; s->get_latency = NULL; @@ -114,35 +177,53 @@ pa_sink* pa_sink_new( s->set_mute = NULL; s->get_mute = NULL; s->set_state = NULL; + s->request_rewind = NULL; + s->update_requested_latency = NULL; s->userdata = NULL; s->asyncmsgq = NULL; s->rtpoll = NULL; - s->silence = NULL; + s->silence = pa_silence_memblock_new(core->mempool, &s->sample_spec, 0); + + s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); + s->thread_info.soft_volume = s->volume; + s->thread_info.soft_muted = s->muted; + s->thread_info.state = s->state; + s->thread_info.rewind_nbytes = 0; + s->thread_info.max_rewind = 0; + s->thread_info.requested_latency_valid = TRUE; + s->thread_info.requested_latency = 0; pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0); - pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info("Created sink %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); + pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s", + s->index, + s->name, + pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map)); - n = pa_sprintf_malloc("%s.monitor", name); + pa_source_new_data_init(&source_data); + pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec); + pa_source_new_data_set_channel_map(&source_data, &s->channel_map); + source_data.name = pa_sprintf_malloc("%s.monitor", name); + source_data.driver = data->driver; - if (!(s->monitor_source = pa_source_new(core, driver, n, 0, spec, map))) - pa_log_warn("Failed to create monitor source."); - else { - char *d; - s->monitor_source->monitor_of = s; - d = pa_sprintf_malloc("Monitor Source of %s", s->name); - pa_source_set_description(s->monitor_source, d); - pa_xfree(d); - } + d = pa_sprintf_malloc("Monitor Source of %s", s->name); + pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, d); + pa_xfree(d); + pa_proplist_sets(data->proplist, PA_PROP_DEVICE_CLASS, "monitor"); - pa_xfree(n); + s->monitor_source = pa_source_new(core, &source_data, 0); - s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - s->thread_info.soft_volume = s->volume; - s->thread_info.soft_muted = s->muted; - s->thread_info.state = s->state; + pa_source_new_data_done(&source_data); + + if (!s->monitor_source) { + pa_sink_unlink(s); + pa_sink_unref(s); + return NULL; + } + + s->monitor_source->monitor_of = s; return s; } @@ -193,12 +274,22 @@ void pa_sink_put(pa_sink* s) { pa_assert(s->asyncmsgq); pa_assert(s->rtpoll); + if (s->get_volume && s->set_volume) + s->flags |= PA_SINK_HW_VOLUME_CTRL; + else + s->flags = (s->flags & ~PA_SINK_HW_VOLUME_CTRL) | PA_SINK_DECIBEL_VOLUME; + + if (s->get_mute && s->set_mute) + s->flags |= PA_SINK_HW_MUTE_CTRL; + else + s->flags &= ~PA_SINK_HW_MUTE_CTRL; + pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); pa_source_put(s->monitor_source); pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index); - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], s); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s); } void pa_sink_unlink(pa_sink* s) { @@ -241,6 +332,8 @@ void pa_sink_unlink(pa_sink* s) { s->set_mute = NULL; s->get_mute = NULL; s->set_state = NULL; + s->request_rewind = NULL; + s->update_requested_latency = NULL; if (s->monitor_source) pa_source_unlink(s->monitor_source); @@ -279,8 +372,11 @@ static void sink_free(pa_object *o) { pa_memblock_unref(s->silence); pa_xfree(s->name); - pa_xfree(s->description); pa_xfree(s->driver); + + if (s->proplist) + pa_proplist_free(s->proplist); + pa_xfree(s); } @@ -330,6 +426,26 @@ void pa_sink_ping(pa_sink *s) { pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_PING, NULL, 0, NULL, NULL); } +void pa_sink_process_rewind(pa_sink *s) { + pa_sink_input *i; + void *state = NULL; + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + + if (s->thread_info.rewind_nbytes <= 0) + return; + + pa_log_debug("Processing rewind..."); + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { + pa_sink_input_assert_ref(i); + + pa_sink_input_rewind(i, s->thread_info.rewind_nbytes); + } + + s->thread_info.rewind_nbytes = 0; +} + static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsigned maxinfo) { pa_sink_input *i; unsigned n = 0; @@ -344,6 +460,11 @@ static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsi if (pa_sink_input_peek(i, length, &info->chunk, &info->volume) < 0) continue; + if (pa_memblock_is_silence(info->chunk.memblock)) { + pa_memblock_unref(info->chunk.memblock); + continue; + } + info->userdata = pa_sink_input_ref(i); pa_assert(info->chunk.memblock); @@ -427,6 +548,8 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_sink_ref(s); + s->thread_info.rewind_nbytes = 0; + if (length <= 0) length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec); @@ -440,19 +563,8 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { if (n == 0) { - if (length > SILENCE_BUFFER_LENGTH) - length = pa_frame_align(SILENCE_BUFFER_LENGTH, &s->sample_spec); - - pa_assert(length > 0); - - if (!s->silence || pa_memblock_get_length(s->silence) < length) { - if (s->silence) - pa_memblock_unref(s->silence); - s->silence = pa_silence_memblock_new(s->core->mempool, &s->sample_spec, length); - } - result->memblock = pa_memblock_ref(s->silence); - result->length = length; + result->length = PA_MIN(pa_memblock_get_length(s->silence), length); result->index = 0; } else if (n == 1) { @@ -506,11 +618,13 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_sink_ref(s); + s->thread_info.rewind_nbytes = 0; + n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, target->length, info, MAX_MIX_CHANNELS) : 0; - if (n == 0) { + if (n == 0) pa_silence_memchunk(target, &s->sample_spec); - } else if (n == 1) { + else if (n == 1) { if (target->length > info[0].chunk.length) target->length = info[0].chunk.length; @@ -573,6 +687,8 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { pa_sink_ref(s); + s->thread_info.rewind_nbytes = 0; + l = target->length; d = 0; while (l > 0) { @@ -596,6 +712,8 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_assert(pa_frame_aligned(length, &s->sample_spec)); pa_assert(result); + s->thread_info.rewind_nbytes = 0; + /*** This needs optimization ***/ result->index = 0; @@ -614,6 +732,8 @@ void pa_sink_skip(pa_sink *s, size_t length) { pa_assert(length > 0); pa_assert(pa_frame_aligned(length, &s->sample_spec)); + s->thread_info.rewind_nbytes = 0; + if (pa_source_used_by(s->monitor_source)) { pa_memchunk chunk; @@ -644,6 +764,8 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) { pa_sink_assert_ref(s); pa_assert(PA_SINK_LINKED(s->state)); + /* The returned value is supposed to be in the time domain of the sound card! */ + if (!PA_SINK_OPENED(s->state)) return 0; @@ -735,43 +857,34 @@ pa_bool_t pa_sink_get_mute(pa_sink *s) { return s->muted; } -void pa_sink_set_module(pa_sink *s, pa_module *m) { - pa_sink_assert_ref(s); - - if (s->module == m) - return; - - s->module = m; - - if (s->monitor_source) - pa_source_set_module(s->monitor_source, m); - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); -} - void pa_sink_set_description(pa_sink *s, const char *description) { + const char *old; pa_sink_assert_ref(s); - if (!description && !s->description) + if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION)) return; - if (description && s->description && !strcmp(description, s->description)) + old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION); + + if (old && description && !strcmp(old, description)) return; - pa_xfree(s->description); - s->description = pa_xstrdup(description); + if (description) + pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description); + else + pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION); if (s->monitor_source) { char *n; - n = pa_sprintf_malloc("Monitor Source of %s", s->description? s->description : s->name); + n = pa_sprintf_malloc("Monitor Source of %s", description ? description : s->name); pa_source_set_description(s->monitor_source, n); pa_xfree(n); } if (PA_SINK_LINKED(s->state)) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED], s); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s); } } @@ -817,6 +930,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse case PA_SINK_MESSAGE_ADD_INPUT: { pa_sink_input *i = PA_SINK_INPUT(userdata); + pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i)); /* Since the caller sleeps in pa_sink_input_put(), we can @@ -835,6 +949,8 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse i->thread_info.sync_next->thread_info.sync_prev = i; } + pa_sink_input_set_max_rewind(i, s->thread_info.max_rewind); + pa_assert(!i->thread_info.attached); i->thread_info.attached = TRUE; @@ -845,6 +961,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse * ghost sink input handling a few lines down at * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + pa_sink_invalidate_requested_latency(s); + +/* i->thread_info.ignore_rewind = TRUE; */ +/* pa_sink_request_rewind(s, 0); */ + return 0; } @@ -881,6 +1002,10 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index))) pa_sink_input_unref(i); + pa_sink_invalidate_requested_latency(s); + +/* pa_sink_request_rewind(s, 0); */ + return 0; } @@ -899,6 +1024,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_assert(info->sink_input->thread_info.attached); info->sink_input->thread_info.attached = FALSE; + pa_sink_invalidate_requested_latency(info->sink_input->sink); if (info->ghost_sink_input) { pa_assert(info->buffer_bytes > 0); @@ -934,9 +1060,8 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse info->buffer_bytes -= n; } - /* Add the remaining already resampled chunk to the buffer */ - if (info->sink_input->thread_info.resampled_chunk.memblock) - pa_memblockq_push(info->buffer, &info->sink_input->thread_info.resampled_chunk); + /* Add the remaining already resampled chunks to the buffer */ + pa_memblockq_splice(info->buffer, info->sink_input->thread_info.render_memblockq); pa_memblockq_sink_input_set_queue(info->ghost_sink_input, info->buffer); @@ -952,22 +1077,33 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(info->ghost_sink_input->index), pa_sink_input_ref(info->ghost_sink_input)); info->ghost_sink_input->thread_info.sync_prev = info->ghost_sink_input->thread_info.sync_next = NULL; + pa_sink_input_set_max_rewind(info->ghost_sink_input, s->thread_info.max_rewind); + pa_assert(!info->ghost_sink_input->thread_info.attached); info->ghost_sink_input->thread_info.attached = TRUE; if (info->ghost_sink_input->attach) info->ghost_sink_input->attach(info->ghost_sink_input); + } + pa_sink_invalidate_requested_latency(s); + + pa_sink_request_rewind(s, 0); + return 0; } case PA_SINK_MESSAGE_SET_VOLUME: s->thread_info.soft_volume = *((pa_cvolume*) userdata); + + pa_sink_request_rewind(s, 0); return 0; case PA_SINK_MESSAGE_SET_MUTE: s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata); + + pa_sink_request_rewind(s, 0); return 0; case PA_SINK_MESSAGE_GET_VOLUME: @@ -1064,3 +1200,73 @@ void pa_sink_attach_within_thread(pa_sink *s) { if (s->monitor_source) pa_source_attach_within_thread(s->monitor_source); } + +void pa_sink_request_rewind(pa_sink*s, size_t nbytes) { + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->thread_info.state)); + + if (nbytes <= 0) + nbytes = s->thread_info.max_rewind; + + nbytes = PA_MIN(nbytes, s->thread_info.max_rewind); + + if (nbytes <= s->thread_info.rewind_nbytes) + return; + + s->thread_info.rewind_nbytes = nbytes; + + if (s->request_rewind) + s->request_rewind(s); +} + +pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { + pa_usec_t result = 0; + pa_sink_input *i; + void *state = NULL; + + pa_sink_assert_ref(s); + + if (s->thread_info.requested_latency_valid) + return s->thread_info.requested_latency; + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) + + if (i->thread_info.requested_sink_latency > 0 && + (!result || result > i->thread_info.requested_sink_latency)) + result = i->thread_info.requested_sink_latency; + + s->thread_info.requested_latency = result; + s->thread_info.requested_latency_valid = TRUE; + + return result; +} + +void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { + pa_sink_input *i; + void *state = NULL; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->thread_info.state)); + + if (max_rewind == s->thread_info.max_rewind) + return; + + s->thread_info.max_rewind = max_rewind; + + while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) + pa_sink_input_set_max_rewind(i, s->thread_info.max_rewind); +} + +void pa_sink_invalidate_requested_latency(pa_sink *s) { + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->thread_info.state)); + + if (!s->thread_info.requested_latency_valid) + return; + + s->thread_info.requested_latency_valid = FALSE; + + if (s->update_requested_latency) + s->update_requested_latency(s); +} diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index e9969309..3f169952 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -69,7 +69,8 @@ struct pa_sink { pa_sink_flags_t flags; char *name; - char *description, *driver; /* may be NULL */ + char *driver; /* may be NULL */ + pa_proplist *proplist; pa_module *module; /* may be NULL */ @@ -85,16 +86,20 @@ struct pa_sink { pa_bool_t refresh_volume; pa_bool_t refresh_mute; - int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */ - int (*set_volume)(pa_sink *s); /* dito */ - int (*get_volume)(pa_sink *s); /* dito */ - int (*get_mute)(pa_sink *s); /* dito */ - int (*set_mute)(pa_sink *s); /* dito */ - pa_usec_t (*get_latency)(pa_sink *s); /* dito */ - pa_asyncmsgq *asyncmsgq; pa_rtpoll *rtpoll; + pa_memblock *silence; + + int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */ + int (*set_volume)(pa_sink *s); /* dito */ + int (*get_volume)(pa_sink *s); /* dito */ + int (*get_mute)(pa_sink *s); /* dito */ + int (*set_mute)(pa_sink *s); /* dito */ + pa_usec_t (*get_latency)(pa_sink *s); /* dito */ + void (*request_rewind)(pa_sink *s); /* dito */ + void (*update_requested_latency)(pa_sink *s); /* dito */ + /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ struct { @@ -102,9 +107,17 @@ struct pa_sink { pa_hashmap *inputs; pa_cvolume soft_volume; pa_bool_t soft_muted; - } thread_info; - pa_memblock *silence; + pa_bool_t requested_latency_valid; + size_t requested_latency; + + /* The number of bytes we need keep around to be able to satisfy + * every DMA buffer rewrite */ + size_t max_rewind; + + /* Maximum of what clients requested to rewind in this cycle */ + size_t rewind_nbytes; + } thread_info; void *userdata; }; @@ -128,20 +141,43 @@ typedef enum pa_sink_message { PA_SINK_MESSAGE_MAX } pa_sink_message_t; +typedef struct pa_sink_new_data { + char *name; + pa_bool_t namereg_fail; + pa_proplist *proplist; + + const char *driver; + pa_module *module; + + pa_sample_spec sample_spec; + pa_bool_t sample_spec_is_set; + pa_channel_map channel_map; + pa_bool_t channel_map_is_set; + + pa_cvolume volume; + pa_bool_t volume_is_set; + pa_bool_t muted; + pa_bool_t muted_is_set; +} pa_sink_new_data; + +pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data); +void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name); +void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec); +void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map); +void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume); +void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute); +void pa_sink_new_data_done(pa_sink_new_data *data); + /* To be called exclusively by the sink driver, from main context */ pa_sink* pa_sink_new( pa_core *core, - const char *driver, - const char *name, - int namereg_fail, - const pa_sample_spec *spec, - const pa_channel_map *map); + pa_sink_new_data *data, + pa_sink_flags_t flags); void pa_sink_put(pa_sink *s); void pa_sink_unlink(pa_sink* s); -void pa_sink_set_module(pa_sink *sink, pa_module *m); void pa_sink_set_description(pa_sink *s, const char *description); void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q); void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p); @@ -151,12 +187,15 @@ void pa_sink_attach(pa_sink *s); /* May be called by everyone, from main context */ +/* The returned value is supposed to be in the time domain of the sound card! */ pa_usec_t pa_sink_get_latency(pa_sink *s); int pa_sink_update_status(pa_sink*s); int pa_sink_suspend(pa_sink *s, pa_bool_t suspend); int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend); +void pa_sink_rewind(pa_sink *s, size_t length); + /* Sends a ping message to the sink thread, to make it wake up and * check for data to process even if there is no real message is * sent */ @@ -173,11 +212,12 @@ unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are n /* To be called exclusively by the sink driver, from IO context */ +void pa_sink_process_rewind(pa_sink *s); + void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); void pa_sink_render_into(pa_sink*s, pa_memchunk *target); void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); - void pa_sink_skip(pa_sink *s, size_t length); int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); @@ -185,4 +225,14 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse void pa_sink_attach_within_thread(pa_sink *s); void pa_sink_detach_within_thread(pa_sink *s); +pa_usec_t pa_sink_get_requested_latency(pa_sink *s); + +void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind); + +/* To be called exclusively by sink input drivers, from IO context */ + +void pa_sink_request_rewind(pa_sink*s, size_t nbytes); + +void pa_sink_invalidate_requested_latency(pa_sink *s); + #endif diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index bb1f3e9a..60c2560e 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -41,17 +41,21 @@ #include #include #include +#include #include "sound-file-stream.h" +#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) + typedef struct file_stream { pa_msgobject parent; pa_core *core; - SNDFILE *sndfile; pa_sink_input *sink_input; - pa_memchunk memchunk; + + SNDFILE *sndfile; sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); - size_t drop; + + pa_memblockq *memblockq; } file_stream; enum { @@ -69,7 +73,6 @@ static void file_stream_unlink(file_stream *u) { return; pa_sink_input_unlink(u->sink_input); - pa_sink_input_unref(u->sink_input); u->sink_input = NULL; @@ -81,10 +84,8 @@ static void file_stream_free(pa_object *o) { file_stream *u = FILE_STREAM(o); pa_assert(u); - file_stream_unlink(u); - - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); + if (u->memblockq) + pa_memblockq_free(u->memblockq); if (u->sndfile) sf_close(u->sndfile); @@ -106,116 +107,122 @@ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int } static void sink_input_kill_cb(pa_sink_input *i) { + file_stream *u; + pa_sink_input_assert_ref(i); + u = FILE_STREAM(i->userdata); + file_stream_assert_ref(u); - file_stream_unlink(FILE_STREAM(i->userdata)); + file_stream_unlink(u); } -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { file_stream *u; - pa_assert(i); + pa_sink_input_assert_ref(i); pa_assert(chunk); u = FILE_STREAM(i->userdata); file_stream_assert_ref(u); - if (!u->sndfile) + if (!u->memblockq) return -1; - for (;;) { - - if (!u->memchunk.memblock) { + pa_log_debug("pop: %lu", (unsigned long) length); - u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, length); - u->memchunk.index = 0; - - if (u->readf_function) { - sf_count_t n; - void *p; - size_t fs = pa_frame_size(&i->sample_spec); + for (;;) { + pa_memchunk tchunk; - p = pa_memblock_acquire(u->memchunk.memblock); - n = u->readf_function(u->sndfile, p, length/fs); - pa_memblock_release(u->memchunk.memblock); + if (pa_memblockq_peek(u->memblockq, chunk) >= 0) { + pa_memblockq_drop(u->memblockq, chunk->length); + return 0; + } - if (n <= 0) - n = 0; + if (!u->sndfile) + break; - u->memchunk.length = n * fs; - } else { - sf_count_t n; - void *p; + tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length); + tchunk.index = 0; - p = pa_memblock_acquire(u->memchunk.memblock); - n = sf_read_raw(u->sndfile, p, length); - pa_memblock_release(u->memchunk.memblock); + if (u->readf_function) { + sf_count_t n; + void *p; + size_t fs = pa_frame_size(&i->sample_spec); - if (n <= 0) - n = 0; + p = pa_memblock_acquire(tchunk.memblock); + n = u->readf_function(u->sndfile, p, length/fs); + pa_memblock_release(tchunk.memblock); - u->memchunk.length = n; - } + if (n <= 0) + n = 0; - if (u->memchunk.length <= 0) { + tchunk.length = n * fs; - pa_memblock_unref(u->memchunk.memblock); - pa_memchunk_reset(&u->memchunk); + } else { + sf_count_t n; + void *p; - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); + p = pa_memblock_acquire(tchunk.memblock); + n = sf_read_raw(u->sndfile, p, length); + pa_memblock_release(tchunk.memblock); - sf_close(u->sndfile); - u->sndfile = NULL; + if (n <= 0) + n = 0; - return -1; - } + tchunk.length = n; } - pa_assert(u->memchunk.memblock); - pa_assert(u->memchunk.length > 0); + if (tchunk.length <= 0) { + + pa_memblock_unref(tchunk.memblock); - if (u->drop < u->memchunk.length) { - u->memchunk.index += u->drop; - u->memchunk.length -= u->drop; - u->drop = 0; + sf_close(u->sndfile); + u->sndfile = NULL; break; } - u->drop -= u->memchunk.length; - pa_memblock_unref(u->memchunk.memblock); - pa_memchunk_reset(&u->memchunk); + pa_memblockq_push(u->memblockq, &tchunk); + pa_memblock_unref(tchunk.memblock); } - *chunk = u->memchunk; - pa_memblock_ref(chunk->memblock); + pa_log_debug("peek fail"); - pa_assert(chunk->length > 0); - pa_assert(u->drop <= 0); + if (pa_sink_input_safe_to_remove(i)) { + pa_log_debug("completed to play"); - return 0; + pa_memblockq_free(u->memblockq); + u->memblockq = NULL; + + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), FILE_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); + } + + return -1; } -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { file_stream *u; - pa_assert(i); - pa_assert(length > 0); + pa_sink_input_assert_ref(i); + pa_assert(nbytes > 0); u = FILE_STREAM(i->userdata); file_stream_assert_ref(u); - if (u->memchunk.memblock) { + if (!u->memblockq) + return; - if (length < u->memchunk.length) { - u->memchunk.index += length; - u->memchunk.length -= length; - return; - } + pa_memblockq_rewind(u->memblockq, nbytes); +} - length -= u->memchunk.length; - pa_memblock_unref(u->memchunk.memblock); - pa_memchunk_reset(&u->memchunk); - } +static void sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes) { + file_stream *u; - u->drop += length; + pa_sink_input_assert_ref(i); + u = FILE_STREAM(i->userdata); + file_stream_assert_ref(u); + + if (!u->memblockq) + return; + + pa_memblockq_set_maxrewind(u->memblockq, nbytes); } int pa_play_file( @@ -228,6 +235,7 @@ int pa_play_file( pa_sample_spec ss; pa_sink_input_new_data data; int fd; + pa_memblock *silence; pa_assert(sink); pa_assert(fname); @@ -237,10 +245,8 @@ int pa_play_file( u->parent.process_msg = file_stream_process_msg; u->core = sink->core; u->sink_input = NULL; - pa_memchunk_reset(&u->memchunk); u->sndfile = NULL; u->readf_function = NULL; - u->drop = 0; memset(&sfinfo, 0, sizeof(sfinfo)); @@ -312,18 +318,31 @@ int pa_play_file( pa_sink_input_new_data_init(&data); data.sink = sink; data.driver = __FILE__; - data.name = fname; pa_sink_input_new_data_set_sample_spec(&data, &ss); pa_sink_input_new_data_set_volume(&data, volume); + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, fname); + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname); + + u->sink_input = pa_sink_input_new(sink->core, &data, 0); + pa_sink_input_new_data_done(&data); - if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0))) + if (!u->sink_input) goto fail; - u->sink_input->peek = sink_input_peek_cb; - u->sink_input->drop = sink_input_drop_cb; + u->sink_input->pop = sink_input_pop_cb; + u->sink_input->rewind = sink_input_rewind_cb; + u->sink_input->set_max_rewind = sink_input_set_max_rewind; u->sink_input->kill = sink_input_kill_cb; u->sink_input->userdata = u; + silence = pa_silence_memblock_new( + u->core->mempool, + &u->sink_input->sample_spec, + u->sink_input->thread_info.resampler ? pa_resampler_max_block_size(u->sink_input->thread_info.resampler) : 0); + + u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&u->sink_input->sample_spec), 1, 1, 0, silence); + pa_memblock_unref(silence); + pa_sink_input_put(u->sink_input); /* The reference to u is dangling here, because we want to keep diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 88c11469..45a8d74e 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -47,9 +48,18 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d memset(data, 0, sizeof(*data)); data->resample_method = PA_RESAMPLER_INVALID; + data->proplist = pa_proplist_new(); + return data; } +void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) { + pa_assert(data); + + if ((data->sample_spec_is_set = !!spec)) + data->sample_spec = *spec; +} + void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map) { pa_assert(data); @@ -57,11 +67,10 @@ void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, data->channel_map = *map; } -void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec) { +void pa_source_output_new_data_done(pa_source_output_new_data *data) { pa_assert(data); - if ((data->sample_spec_is_set = !!spec)) - data->sample_spec = *spec; + pa_proplist_free(data->proplist); } pa_source_output* pa_source_output_new( @@ -80,7 +89,6 @@ pa_source_output* pa_source_output_new( return NULL; pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); - pa_return_null_if_fail(!data->name || pa_utf8_valid(data->name)); if (!data->source) data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, 1); @@ -156,7 +164,7 @@ pa_source_output* pa_source_output_new( o->core = core; o->state = PA_SOURCE_OUTPUT_INIT; o->flags = flags; - o->name = pa_xstrdup(data->name); + o->proplist = pa_proplist_copy(data->proplist); o->driver = pa_xstrdup(data->driver); o->module = data->module; o->source = data->source; @@ -179,13 +187,14 @@ pa_source_output* pa_source_output_new( o->thread_info.attached = FALSE; o->thread_info.sample_spec = o->sample_spec; o->thread_info.resampler = resampler; + o->thread_info.requested_source_latency = 0; pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s", o->index, - o->name, + pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)), o->source->name, pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map)); @@ -210,7 +219,6 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t o->source->n_corked++; pa_source_update_status(o->source); - o->state = state; if (state != PA_SOURCE_OUTPUT_UNLINKED) @@ -269,14 +277,16 @@ static void source_output_free(pa_object* mo) { if (PA_SOURCE_OUTPUT_LINKED(o->state)) pa_source_output_unlink(o); - pa_log_info("Freeing output %u \"%s\"", o->index, o->name); + pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME))); pa_assert(!o->thread_info.attached); if (o->thread_info.resampler) pa_resampler_free(o->thread_info.resampler); - pa_xfree(o->name); + if (o->proplist) + pa_proplist_free(o->proplist); + pa_xfree(o->driver); pa_xfree(o); } @@ -292,11 +302,10 @@ void pa_source_output_put(pa_source_output *o) { if (o->state == PA_SOURCE_OUTPUT_CORKED) o->source->n_corked++; - pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); pa_source_update_status(o->source); + pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o); } @@ -330,7 +339,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); pa_assert(chunk); - pa_assert(chunk->length); + pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec)); if (!o->push || o->state == PA_SOURCE_OUTPUT_CORKED) return; @@ -351,6 +360,14 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_memblock_unref(rchunk.memblock); } +void pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { + pa_source_output_assert_ref(o); + pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + + pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); +} + + void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); @@ -375,19 +392,24 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { } void pa_source_output_set_name(pa_source_output *o, const char *name) { + const char *old; pa_source_output_assert_ref(o); - if (!o->name && !name) + old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME); + + if (!old && !name) return; - if (o->name && name && !strcmp(o->name, name)) + if (old && name && !strcmp(old, name)) return; - pa_xfree(o->name); - o->name = pa_xstrdup(name); + if (name) + pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name); + else + pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME); if (PA_SOURCE_OUTPUT_LINKED(o->state)) { - pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NAME_CHANGED], o); + pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } } @@ -509,6 +531,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int return 0; } + + case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: + + o->thread_info.requested_source_latency = (pa_usec_t) offset; + pa_source_invalidate_requested_latency(o->source); + + return 0; } return -1; diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index d6da8d00..dc95217b 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -62,10 +62,12 @@ struct pa_source_output { uint32_t index; pa_core *core; + pa_source_output_state_t state; pa_source_output_flags_t flags; - char *name, *driver; /* may be NULL */ + pa_proplist *proplist; + char *driver; /* may be NULL */ pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ @@ -74,6 +76,8 @@ struct pa_source_output { pa_sample_spec sample_spec; pa_channel_map channel_map; + pa_resample_method_t resample_method; + /* Pushes a new memchunk into the output. Called from IO thread * context. */ void (*push)(pa_source_output *o, const pa_memchunk *chunk); @@ -86,14 +90,14 @@ struct pa_source_output { * disconnected from its source. Called from IO thread context */ void (*detach) (pa_source_output *o); /* may be NULL */ - /* If non-NULL called whenever the the source this output is attached - * to changes. Called from main context */ - void (*moved) (pa_source_output *o); /* may be NULL */ - /* If non-NULL called whenever the the source this output is attached * to suspends or resumes. Called from main context */ void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */ + /* If non-NULL called whenever the the source this output is attached + * to changes. Called from main context */ + void (*moved) (pa_source_output *o); /* may be NULL */ + /* Supposed to unlink and destroy this stream. Called from main * context. */ void (*kill)(pa_source_output* o); /* may be NULL */ @@ -104,8 +108,6 @@ struct pa_source_output { thread instead. */ pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ - pa_resample_method_t resample_method; - struct { pa_source_output_state_t state; @@ -114,6 +116,9 @@ struct pa_source_output { pa_sample_spec sample_spec; pa_resampler* resampler; /* may be NULL */ + + /* The requested latency for the source */ + pa_usec_t requested_source_latency; } thread_info; void *userdata; @@ -126,11 +131,14 @@ enum { PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, + PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, PA_SOURCE_OUTPUT_MESSAGE_MAX }; typedef struct pa_source_output_new_data { - const char *name, *driver; + pa_proplist *proplist; + + const char *driver; pa_module *module; pa_client *client; @@ -152,7 +160,7 @@ typedef struct pa_source_output_move_hook_data { pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data); void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec); void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map); -void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume); +void pa_source_output_new_data_done(pa_source_output_new_data *data); /* To be called by the implementing module only */ @@ -166,6 +174,8 @@ void pa_source_output_unlink(pa_source_output*o); void pa_source_output_set_name(pa_source_output *i, const char *name); +void pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec); + /* Callable by everyone */ /* External code may request disconnection with this funcion */ @@ -186,6 +196,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest); /* To be used exclusively by the source driver thread */ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); + int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk); #endif diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index d707ad86..cc1c531d 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -45,35 +45,97 @@ static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject); static void source_free(pa_object *o); +pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) { + pa_assert(data); + + memset(data, 0, sizeof(*data)); + data->proplist = pa_proplist_new(); + + return data; +} + +void pa_source_new_data_set_name(pa_source_new_data *data, const char *name) { + pa_assert(data); + + pa_xfree(data->name); + data->name = pa_xstrdup(name); +} + +void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec) { + pa_assert(data); + + if ((data->sample_spec_is_set = !!spec)) + data->sample_spec = *spec; +} + +void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map) { + pa_assert(data); + + if ((data->channel_map_is_set = !!map)) + data->channel_map = *map; +} + +void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) { + pa_assert(data); + + if ((data->volume_is_set = !!volume)) + data->volume = *volume; +} + +void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) { + pa_assert(data); + + data->muted_is_set = TRUE; + data->muted = !!mute; +} + +void pa_source_new_data_done(pa_source_new_data *data) { + pa_assert(data); + + pa_xfree(data->name); + pa_proplist_free(data->proplist); +} + pa_source* pa_source_new( pa_core *core, - const char *driver, - const char *name, - int fail, - const pa_sample_spec *spec, - const pa_channel_map *map) { + pa_source_new_data *data, + pa_source_flags_t flags) { pa_source *s; - char st[256]; - pa_channel_map tmap; + const char *name; + char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_assert(core); - pa_assert(name); - pa_assert(spec); - pa_return_null_if_fail(pa_sample_spec_valid(spec)); + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) + return NULL; + + pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); + pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); + + pa_return_null_if_fail(data->sample_spec_is_set && pa_sample_spec_valid(&data->sample_spec)); - if (!map) - pa_return_null_if_fail(map = pa_channel_map_init_auto(&tmap, spec->channels, PA_CHANNEL_MAP_DEFAULT)); + if (!data->channel_map_is_set) + pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT)); - pa_return_null_if_fail(map && pa_channel_map_valid(map)); - pa_return_null_if_fail(map->channels == spec->channels); - pa_return_null_if_fail(!driver || pa_utf8_valid(driver)); - pa_return_null_if_fail(pa_utf8_valid(name) && *name); + pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map)); + pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels); + + if (!data->volume_is_set) + pa_cvolume_reset(&data->volume, data->sample_spec.channels); + + pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); + pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels); + + if (!data->muted_is_set) + data->muted = FALSE; + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) + return NULL; s = pa_msgobject_new(pa_source); - if (!(name = pa_namereg_register(core, name, PA_NAMEREG_SOURCE, s, fail))) { + if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) { pa_xfree(s); return NULL; } @@ -83,21 +145,21 @@ pa_source* pa_source_new( s->core = core; s->state = PA_SOURCE_INIT; - s->flags = 0; + s->flags = flags; s->name = pa_xstrdup(name); - s->description = NULL; - s->driver = pa_xstrdup(driver); - s->module = NULL; + s->proplist = pa_proplist_copy(data->proplist); + s->driver = pa_xstrdup(data->driver); + s->module = data->module; - s->sample_spec = *spec; - s->channel_map = *map; + s->sample_spec = data->sample_spec; + s->channel_map = data->channel_map; s->outputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; s->monitor_of = NULL; - pa_cvolume_reset(&s->volume, spec->channels); - s->muted = FALSE; + s->volume = data->volume; + s->muted = data->muted; s->refresh_volume = s->refresh_muted = FALSE; s->get_latency = NULL; @@ -106,20 +168,26 @@ pa_source* pa_source_new( s->set_mute = NULL; s->get_mute = NULL; s->set_state = NULL; + s->update_requested_latency = NULL; s->userdata = NULL; s->asyncmsgq = NULL; s->rtpoll = NULL; - pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0); - - pa_sample_spec_snprint(st, sizeof(st), spec); - pa_log_info("Created source %u \"%s\" with sample spec \"%s\"", s->index, s->name, st); - s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); s->thread_info.soft_volume = s->volume; s->thread_info.soft_muted = s->muted; s->thread_info.state = s->state; + s->thread_info.requested_latency_valid = TRUE; + s->thread_info.requested_latency = 0; + + pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0); + + pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s", + s->index, + s->name, + pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec), + pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map)); return s; } @@ -170,10 +238,26 @@ void pa_source_put(pa_source *s) { pa_assert(s->rtpoll); pa_assert(s->asyncmsgq); + if (s->get_volume && s->set_volume) + s->flags |= PA_SOURCE_HW_VOLUME_CTRL; + else { + s->get_volume = NULL; + s->set_volume = NULL; + s->flags = (s->flags & ~PA_SOURCE_HW_VOLUME_CTRL) | PA_SOURCE_DECIBEL_VOLUME; + } + + if (s->get_mute && s->set_mute) + s->flags |= PA_SOURCE_HW_MUTE_CTRL; + else { + s->get_mute = NULL; + s->set_mute = NULL; + s->flags &= ~PA_SOURCE_HW_MUTE_CTRL; + } + pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index); - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], s); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PUT], s); } void pa_source_unlink(pa_source *s) { @@ -211,6 +295,7 @@ void pa_source_unlink(pa_source *s) { s->set_mute = NULL; s->get_mute = NULL; s->set_state = NULL; + s->update_requested_latency = NULL; if (linked) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); @@ -238,8 +323,11 @@ static void source_free(pa_object *o) { pa_hashmap_free(s->thread_info.outputs, NULL, NULL); pa_xfree(s->name); - pa_xfree(s->description); pa_xfree(s->driver); + + if (s->proplist) + pa_proplist_free(s->proplist); + pa_xfree(s); } @@ -400,32 +488,25 @@ pa_bool_t pa_source_get_mute(pa_source *s) { return s->muted; } -void pa_source_set_module(pa_source *s, pa_module *m) { - pa_source_assert_ref(s); - - if (m == s->module) - return; - - s->module = m; - - pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); -} - void pa_source_set_description(pa_source *s, const char *description) { + const char *old; pa_source_assert_ref(s); - if (!description && !s->description) + if (!description && !pa_proplist_contains(s->proplist, PA_PROP_DEVICE_DESCRIPTION)) return; - if (description && s->description && !strcmp(description, s->description)) + old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION); + if (old && description && !strcmp(old, description)) return; - pa_xfree(s->description); - s->description = pa_xstrdup(description); + if (description) + pa_proplist_sets(s->proplist, PA_PROP_DEVICE_DESCRIPTION, description); + else + pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION); if (PA_SOURCE_LINKED(s->state)) { - pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], s); pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); + pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s); } } @@ -470,6 +551,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ switch ((pa_source_message_t) code) { case PA_SOURCE_MESSAGE_ADD_OUTPUT: { pa_source_output *o = PA_SOURCE_OUTPUT(userdata); + pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o)); pa_assert(!o->thread_info.attached); @@ -478,6 +560,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ if (o->attach) o->attach(o); + pa_source_invalidate_requested_latency(s); + return 0; } @@ -493,6 +577,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ if (pa_hashmap_remove(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index))) pa_source_output_unref(o); + pa_source_invalidate_requested_latency(s); + return 0; } @@ -590,5 +676,40 @@ void pa_source_attach_within_thread(pa_source *s) { while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) if (o->attach) o->attach(o); +} + +pa_usec_t pa_source_get_requested_latency(pa_source *s) { + pa_usec_t result = 0; + pa_source_output *o; + void *state = NULL; + + pa_source_assert_ref(s); + + if (s->thread_info.requested_latency_valid) + return s->thread_info.requested_latency; + + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + + if (o->thread_info.requested_source_latency > 0 && + (!result || result > o->thread_info.requested_source_latency)) + result = o->thread_info.requested_source_latency; + + s->thread_info.requested_latency = result; + s->thread_info.requested_latency_valid = TRUE; + + return result; +} + +void pa_source_invalidate_requested_latency(pa_source *s) { + + pa_source_assert_ref(s); + pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + + if (!s->thread_info.requested_latency_valid) + return; + + s->thread_info.requested_latency_valid = FALSE; + if (s->update_requested_latency) + s->update_requested_latency(s); } diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index bd0a9122..c880d3c5 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -71,7 +71,8 @@ struct pa_source { pa_source_flags_t flags; char *name; - char *description, *driver; /* may be NULL */ + char *driver; /* may be NULL */ + pa_proplist *proplist; pa_module *module; /* may be NULL */ @@ -87,15 +88,16 @@ struct pa_source { pa_bool_t refresh_volume; pa_bool_t refresh_muted; + pa_asyncmsgq *asyncmsgq; + pa_rtpoll *rtpoll; + int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */ int (*set_volume)(pa_source *s); /* dito */ int (*get_volume)(pa_source *s); /* dito */ int (*set_mute)(pa_source *s); /* dito */ int (*get_mute)(pa_source *s); /* dito */ pa_usec_t (*get_latency)(pa_source *s); /* dito */ - - pa_asyncmsgq *asyncmsgq; - pa_rtpoll *rtpoll; + void (*update_requested_latency)(pa_source *s); /* dito */ /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ @@ -104,6 +106,9 @@ struct pa_source { pa_hashmap *outputs; pa_cvolume soft_volume; pa_bool_t soft_muted; + + pa_bool_t requested_latency_valid; + size_t requested_latency; } thread_info; void *userdata; @@ -127,20 +132,43 @@ typedef enum pa_source_message { PA_SOURCE_MESSAGE_MAX } pa_source_message_t; +typedef struct pa_source_new_data { + char *name; + pa_bool_t namereg_fail; + pa_proplist *proplist; + + const char *driver; + pa_module *module; + + pa_sample_spec sample_spec; + pa_bool_t sample_spec_is_set; + pa_channel_map channel_map; + pa_bool_t channel_map_is_set; + + pa_cvolume volume; + pa_bool_t volume_is_set; + pa_bool_t muted; + pa_bool_t muted_is_set; +} pa_source_new_data; + +pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data); +void pa_source_new_data_set_name(pa_source_new_data *data, const char *name); +void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec); +void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map); +void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume); +void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute); +void pa_source_new_data_done(pa_source_new_data *data); + /* To be called exclusively by the source driver, from main context */ pa_source* pa_source_new( pa_core *core, - const char *driver, - const char *name, - int namereg_fail, - const pa_sample_spec *spec, - const pa_channel_map *map); + pa_source_new_data *data, + pa_source_flags_t flags); void pa_source_put(pa_source *s); void pa_source_unlink(pa_source *s); -void pa_source_set_module(pa_source *s, pa_module *m); void pa_source_set_description(pa_source *s, const char *description); void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q); void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p); @@ -176,4 +204,10 @@ int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa void pa_source_attach_within_thread(pa_source *s); void pa_source_detach_within_thread(pa_source *s); +pa_usec_t pa_source_get_requested_latency(pa_source *s); + +/* To be called exclusively by source output drivers, from IO context */ + +void pa_source_invalidate_requested_latency(pa_source *s); + #endif diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c index 556fe806..0017c388 100644 --- a/src/pulsecore/tagstruct.c +++ b/src/pulsecore/tagstruct.c @@ -42,12 +42,14 @@ #include "tagstruct.h" +#define MAX_TAG_SIZE (64*1024) + struct pa_tagstruct { uint8_t *data; size_t length, allocated; size_t rindex; - int dynamic; + pa_bool_t dynamic; }; pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length) { @@ -254,6 +256,32 @@ void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume) { } } +void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) { + void *state = NULL; + pa_assert(t); + pa_assert(p); + + extend(t, 1); + + t->data[t->length++] = PA_TAG_PROPLIST; + + for (;;) { + const char *k; + const void *d; + size_t l; + + if (!(k = pa_proplist_iterate(p, &state))) + break; + + pa_tagstruct_puts(t, k); + pa_assert_se(pa_proplist_get(p, k, &d, &l) >= 0); + pa_tagstruct_putu32(t, (uint32_t) l); + pa_tagstruct_put_arbitrary(t, d, l); + } + + pa_tagstruct_puts(t, NULL); +} + int pa_tagstruct_gets(pa_tagstruct*t, const char **s) { int error = 0; size_t n; @@ -529,6 +557,57 @@ int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *cvolume) { return 0; } +int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) { + size_t saved_rindex; + + pa_assert(t); + pa_assert(p); + + if (t->rindex+1 > t->length) + return -1; + + if (t->data[t->rindex] != PA_TAG_PROPLIST) + return -1; + + saved_rindex = t->rindex; + + for (;;) { + const char *k; + void *d; + uint32_t length; + + if (pa_tagstruct_gets(t, &k) < 0) + goto fail; + + if (!k) + break; + + if (pa_tagstruct_getu32(t, &length) < 0) + goto fail; + + if (length > MAX_TAG_SIZE) + goto fail; + + d = pa_xmalloc(length); + + if (pa_tagstruct_get_arbitrary(t, d, length) < 0) + goto fail; + + if (pa_proplist_set(p, k, d, length) < 0) { + pa_xfree(d); + goto fail; + } + + pa_xfree(d); + } + + return 0; + +fail: + t->rindex = saved_rindex; + return -1; +} + void pa_tagstruct_put(pa_tagstruct *t, ...) { va_list va; pa_assert(t); @@ -591,6 +670,9 @@ void pa_tagstruct_put(pa_tagstruct *t, ...) { pa_tagstruct_put_cvolume(t, va_arg(va, pa_cvolume *)); break; + case PA_TAG_PROPLIST: + pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *)); + default: pa_assert_not_reached(); } @@ -662,6 +744,9 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) { ret = pa_tagstruct_get_cvolume(t, va_arg(va, pa_cvolume *)); break; + case PA_TAG_PROPLIST: + ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *)); + default: pa_assert_not_reached(); } diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h index e9bb9ac8..3b2ce7b9 100644 --- a/src/pulsecore/tagstruct.h +++ b/src/pulsecore/tagstruct.h @@ -32,6 +32,7 @@ #include #include #include +#include typedef struct pa_tagstruct pa_tagstruct; @@ -51,7 +52,8 @@ enum { PA_TAG_TIMEVAL = 'T', PA_TAG_USEC = 'U' /* 64bit unsigned */, PA_TAG_CHANNEL_MAP = 'm', - PA_TAG_CVOLUME = 'v' + PA_TAG_CVOLUME = 'v', + PA_TAG_PROPLIST = 'P' }; pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length); @@ -75,6 +77,7 @@ void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv); void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u); void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map); void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume); +void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p); int pa_tagstruct_get(pa_tagstruct *t, ...); @@ -90,6 +93,7 @@ int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv); int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u); int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map); int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v); +int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p); #endif diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index 4cebded4..b44aaa50 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -332,6 +332,8 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { s->py = y + s->dp *s->adjust_time; s->abc_valid = FALSE; + + pa_log_debug("put(%llu | %llu) = %llu", x + s->time_offset, x, y); } pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { @@ -350,6 +352,9 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { pa_assert(x >= s->ex); estimate(s, x, &y, NULL); + + pa_log_debug("get(%llu | %llu) = %llu", x + s->time_offset, x, y); + return y; } @@ -357,6 +362,8 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) { pa_assert(s); s->time_offset = offset; + + pa_log_debug("offset(%llu)", offset); } void pa_smoother_pause(pa_smoother *s, pa_usec_t x) { @@ -365,6 +372,8 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) { if (s->paused) return; + pa_log_debug("pause(%llu)", x); + s->paused = TRUE; s->pause_time = x; } @@ -377,6 +386,31 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) { pa_assert(x >= s->pause_time); + pa_log_debug("resume(%llu)", x); + s->paused = FALSE; s->time_offset += x - s->pause_time; } + +pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) { + pa_usec_t ney; + double nde; + + pa_assert(s); + pa_assert(x >= s->time_offset); + + /* Fix up x value */ + if (s->paused) + x = s->pause_time; + + pa_assert(x >= s->time_offset); + x -= s->time_offset; + + pa_assert(x >= s->ex); + + estimate(s, x, &ney, &nde); + + pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / s->dp), s->dp); + + return (pa_usec_t) ((double) y_delay / s->dp); +} diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h index 8b8512e2..85d9f0fd 100644 --- a/src/pulsecore/time-smoother.h +++ b/src/pulsecore/time-smoother.h @@ -29,13 +29,19 @@ typedef struct pa_smoother pa_smoother; -pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic); +pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic); void pa_smoother_free(pa_smoother* s); +/* Adds a new value to our dataset. x = local/system time, y = remote time */ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y); + +/* Returns an interpolated value based on the dataset. x = local/system time, return value = remote time */ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x); -void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset); +/* Translates a time span from the remote time domain to the local one. x = local/system time when to estimate, y_delay = remote time span */ +pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay); + +void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t x_offset); void pa_smoother_pause(pa_smoother *s, pa_usec_t x); void pa_smoother_resume(pa_smoother *s, pa_usec_t x); -- cgit From ebecf3d3e19ac56f507ce771d5c455198280a80c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Mar 2008 15:21:26 +0000 Subject: commit glitch-free work git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2123 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/memblockq-test.c | 59 +++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c index 25ea399b..402c8579 100644 --- a/src/tests/memblockq-test.c +++ b/src/tests/memblockq-test.c @@ -31,6 +31,30 @@ #include #include +static void dump(pa_memblockq *bq) { + printf(">"); + + for (;;) { + pa_memchunk out; + char *e; + size_t n; + void *q; + + if (pa_memblockq_peek(bq, &out) < 0) + break; + + q = pa_memblock_acquire(out.memblock); + for (e = (char*) q + out.index, n = 0; n < out.length; n++) + printf("%c", *e); + pa_memblock_release(out.memblock); + + pa_memblock_unref(out.memblock); + pa_memblockq_drop(bq, out.length); + } + + printf("<\n"); +} + int main(int argc, char *argv[]) { int ret; @@ -46,7 +70,7 @@ int main(int argc, char *argv[]) { silence = pa_memblock_new_fixed(p, (char*) "__", 2, 1); assert(silence); - bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, silence); + bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, 40, silence); assert(bq); chunk1.memblock = pa_memblock_new_fixed(p, (char*) "11", 2, 1); @@ -72,13 +96,13 @@ int main(int argc, char *argv[]) { ret = pa_memblockq_push(bq, &chunk1); assert(ret == 0); - ret = pa_memblockq_push(bq, &chunk1); + ret = pa_memblockq_push(bq, &chunk2); assert(ret == 0); - ret = pa_memblockq_push(bq, &chunk2); + ret = pa_memblockq_push(bq, &chunk3); assert(ret == 0); - ret = pa_memblockq_push(bq, &chunk2); + ret = pa_memblockq_push(bq, &chunk4); assert(ret == 0); pa_memblockq_seek(bq, -6, 0); @@ -86,7 +110,7 @@ int main(int argc, char *argv[]) { assert(ret == 0); pa_memblockq_seek(bq, -2, 0); - ret = pa_memblockq_push(bq, &chunk3); + ret = pa_memblockq_push(bq, &chunk1); assert(ret == 0); pa_memblockq_seek(bq, -10, 0); @@ -119,28 +143,13 @@ int main(int argc, char *argv[]) { ret = pa_memblockq_push(bq, &chunk3); assert(ret == 0); - pa_memblockq_shorten(bq, pa_memblockq_get_length(bq)-2); - - printf(">"); - - for (;;) { - pa_memchunk out; - char *e; - size_t n; - - if (pa_memblockq_peek(bq, &out) < 0) - break; + pa_memblockq_seek(bq, 30, PA_SEEK_RELATIVE); - p = pa_memblock_acquire(out.memblock); - for (e = (char*) p + out.index, n = 0; n < out.length; n++) - printf("%c", *e); - pa_memblock_release(out.memblock); + dump(bq); - pa_memblock_unref(out.memblock); - pa_memblockq_drop(bq, out.length); - } + pa_memblockq_rewind(bq, 52); - printf("<\n"); + dump(bq); pa_memblockq_free(bq); pa_memblock_unref(silence); @@ -149,5 +158,7 @@ int main(int argc, char *argv[]) { pa_memblock_unref(chunk3.memblock); pa_memblock_unref(chunk4.memblock); + pa_mempool_free(p); + return 0; } -- cgit From d6bd152b4d5072f8af22644b7e2cdfa427a7fb86 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Mar 2008 15:21:41 +0000 Subject: commit glitch-free work git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2124 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.c | 133 +++++++- src/modules/module-alsa-sink.c | 610 +++++++++++++++++++++++++++------- src/modules/module-alsa-source.c | 321 +++++++++++++----- src/modules/module-combine.c | 66 ++-- src/modules/module-esound-sink.c | 14 +- src/modules/module-hal-detect.c | 8 +- src/modules/module-jack-sink.c | 25 +- src/modules/module-jack-source.c | 25 +- src/modules/module-ladspa-sink.c | 194 ++++++----- src/modules/module-match.c | 7 +- src/modules/module-null-sink.c | 16 +- src/modules/module-oss.c | 62 ++-- src/modules/module-pipe-sink.c | 20 +- src/modules/module-pipe-source.c | 20 +- src/modules/module-remap-sink.c | 132 +++++--- src/modules/module-rescue-streams.c | 8 +- src/modules/module-sine.c | 46 +-- src/modules/module-suspend-on-idle.c | 4 +- src/modules/module-tunnel.c | 35 +- src/modules/module-volume-restore.c | 6 +- src/modules/module-x11-bell.c | 2 +- src/modules/module-zeroconf-publish.c | 16 +- 22 files changed, 1266 insertions(+), 504 deletions(-) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 6afec3bc..d899eaec 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -27,6 +27,7 @@ #endif #include +#include #include #include @@ -290,16 +291,22 @@ int pa_alsa_set_hw_params( pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, + pa_bool_t *use_tsched, pa_bool_t require_exact_channel_number) { int ret = -1; + snd_pcm_uframes_t _period_size = *period_size; + unsigned int _periods = *periods; snd_pcm_uframes_t buffer_size; unsigned int r = ss->rate; unsigned int c = ss->channels; pa_sample_format_t f = ss->format; snd_pcm_hw_params_t *hwparams; pa_bool_t _use_mmap = use_mmap && *use_mmap; + pa_bool_t _use_tsched = use_tsched && *use_tsched; + int dir; pa_assert(pcm_handle); pa_assert(ss); @@ -308,8 +315,6 @@ int pa_alsa_set_hw_params( snd_pcm_hw_params_alloca(&hwparams); - buffer_size = *periods * *period_size; - if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) goto finish; @@ -330,12 +335,19 @@ int pa_alsa_set_hw_params( } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) goto finish; + if (!_use_mmap) + _use_tsched = FALSE; + if ((ret = set_format(pcm_handle, hwparams, &f)) < 0) goto finish; if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) goto finish; + /* Adjust the buffer sizes, if we didn't get the rate we were asking for */ + _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate); + tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); + if (require_exact_channel_number) { if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) goto finish; @@ -344,10 +356,32 @@ int pa_alsa_set_hw_params( goto finish; } - if ((*period_size > 0 && (ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, period_size, NULL)) < 0) || - (*periods > 0 && (ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)) + if (_use_tsched) { + _period_size = tsched_size; + _periods = 1; + + pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0); + pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r); + } + + buffer_size = _periods * _period_size; + + if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) goto finish; + if (_periods > 0) { + dir = 1; + if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) { + dir = -1; + if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) + goto finish; + } + } + + if (_period_size > 0) + if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) + goto finish; + if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) goto finish; @@ -363,8 +397,8 @@ int pa_alsa_set_hw_params( if ((ret = snd_pcm_prepare(pcm_handle)) < 0) goto finish; - if ((ret = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size)) < 0 || - (ret = snd_pcm_hw_params_get_period_size(hwparams, period_size, NULL)) < 0) + if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || + (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0) goto finish; /* If the sample rate deviates too much, we need to resample */ @@ -373,14 +407,18 @@ int pa_alsa_set_hw_params( ss->channels = c; ss->format = f; - pa_assert(buffer_size > 0); - pa_assert(*period_size > 0); - *periods = buffer_size / *period_size; - pa_assert(*periods > 0); + pa_assert(_periods > 0); + pa_assert(_period_size > 0); + + *periods = _periods; + *period_size = _period_size; if (use_mmap) *use_mmap = _use_mmap; + if (use_tsched) + *use_tsched = _use_tsched; + ret = 0; finish: @@ -388,7 +426,7 @@ finish: return ret; } -int pa_alsa_set_sw_params(snd_pcm_t *pcm) { +int pa_alsa_set_sw_params(snd_pcm_t *pcm, size_t avail_min) { snd_pcm_sw_params_t *swparams; int err; @@ -411,6 +449,11 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm) { return err; } + if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) { + pa_log_error("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err)); + return err; + } + if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) { pa_log_warn("Unable to set sw params: %s\n", snd_strerror(err)); return err; @@ -477,7 +520,9 @@ snd_pcm_t *pa_alsa_open_by_device_id( int mode, uint32_t *nfrags, snd_pcm_uframes_t *period_size, - pa_bool_t *use_mmap) { + snd_pcm_uframes_t tsched_size, + pa_bool_t *use_mmap, + pa_bool_t *use_tsched) { int i; int direction = 1; @@ -536,7 +581,7 @@ snd_pcm_t *pa_alsa_open_by_device_id( try_ss.rate = ss->rate; try_ss.format = ss->format; - if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, use_mmap, TRUE)) < 0) { + if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) { pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err)); pa_xfree(d); snd_pcm_close(pcm_handle); @@ -554,7 +599,7 @@ snd_pcm_t *pa_alsa_open_by_device_id( d = pa_sprintf_malloc("hw:%s", dev_id); pa_log_debug("Trying %s as last resort...", d); - pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, use_mmap); + pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched); pa_xfree(d); return pcm_handle; @@ -568,7 +613,9 @@ snd_pcm_t *pa_alsa_open_by_device_string( int mode, uint32_t *nfrags, snd_pcm_uframes_t *period_size, - pa_bool_t *use_mmap) { + snd_pcm_uframes_t tsched_size, + pa_bool_t *use_mmap, + pa_bool_t *use_tsched) { int err; char *d; @@ -591,7 +638,7 @@ snd_pcm_t *pa_alsa_open_by_device_string( return NULL; } - if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, use_mmap, FALSE)) < 0) { + if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) { if (err == -EPERM) { /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */ @@ -773,7 +820,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel } if ((is_mono && mono_used) || (!is_mono && alsa_channel_used[id])) { - pa_log_info("Channel map has duplicate channel '%s', failling back to software volume control.", pa_channel_position_to_string(channel_map->map[i])); + pa_log_info("Channel map has duplicate channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i])); return -1; } @@ -793,7 +840,57 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel } } - pa_log_info("All %u channels can be mapped to mixer channels. Using hardware volume control.", channel_map->channels); + pa_log_info("All %u channels can be mapped to mixer channels.", channel_map->channels); return 0; } + +void pa_alsa_0dB_playback(snd_mixer_elem_t *elem) { + long min, max, v; + + pa_assert(elem); + + /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use + * raw volume levels and fix them to 75% */ + + if (snd_mixer_selem_set_playback_dB_all(elem, 0, -1) >= 0) + return; + + if (snd_mixer_selem_set_playback_dB_all(elem, 0, 1) >= 0) + return; + + if (snd_mixer_selem_get_playback_volume_range(elem, &min, &max) < 0) + return; + + v = min + ((max - min) * 3) / 4; /* 75% */ + + if (v <= min) + v = max; + + snd_mixer_selem_set_playback_volume_all(elem, v); +} + +void pa_alsa_0dB_capture(snd_mixer_elem_t *elem) { + long min, max, v; + + pa_assert(elem); + + /* Try to enable 0 dB if possible. If ALSA cannot do dB, then use + * raw volume levels and fix them to 75% */ + + if (snd_mixer_selem_set_capture_dB_all(elem, 0, -1) >= 0) + return; + + if (snd_mixer_selem_set_capture_dB_all(elem, 0, 1) >= 0) + return; + + if (snd_mixer_selem_get_capture_volume_range(elem, &min, &max) < 0) + return; + + v = min + ((max - min) * 3) / 4; /* 75% */ + + if (v <= min) + v = max; + + snd_mixer_selem_set_capture_volume_all(elem, v); +} diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 14aef7c9..7e60cead 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -46,6 +47,8 @@ #include #include #include +#include +#include #include "alsa-util.h" #include "module-alsa-sink-symdef.h" @@ -59,14 +62,19 @@ PA_MODULE_USAGE( "device= " "device_id= " "format= " - "channels= " "rate= " + "channels= " + "channel_map= " "fragments= " "fragment_size= " - "channel_map= " - "mmap="); + "mmap= " + "tsched= " + "tsched_buffer_size= " + "tsched_buffer_watermark="); #define DEFAULT_DEVICE "default" +#define DEFAULT_TSCHED_BUFFER_USEC (3*PA_USEC_PER_SEC) +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) struct userdata { pa_core *core; @@ -83,33 +91,43 @@ struct userdata { snd_mixer_t *mixer_handle; snd_mixer_elem_t *mixer_elem; long hw_volume_max, hw_volume_min; + long hw_dB_max, hw_dB_min; + pa_bool_t hw_dB_supported; - size_t frame_size, fragment_size, hwbuf_size; + size_t frame_size, fragment_size, hwbuf_size, tsched_watermark; unsigned nfragments; pa_memchunk memchunk; char *device_name; - pa_bool_t use_mmap; + pa_bool_t use_mmap, use_tsched; pa_bool_t first; pa_rtpoll_item *alsa_rtpoll_item; snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST]; + + pa_smoother *smoother; + int64_t frame_index; + + snd_pcm_sframes_t hwbuf_unused_frames; }; static const char* const valid_modargs[] = { + "sink_name", "device", "device_id", - "sink_name", "format", - "channels", "rate", + "channels", + "channel_map", "fragments", "fragment_size", - "channel_map", "mmap", + "tsched", + "tsched_buffer_size", + "tsched_buffer_watermark", NULL }; @@ -127,7 +145,12 @@ static int mmap_write(struct userdata *u) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset, frames; - if ((n = snd_pcm_avail_update(u->pcm_handle)) < 0) { + /* First we determine how many samples are missing to fill the + * buffer up to 100% */ + + if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { + + pa_log_debug("snd_pcm_avail_update: %s", snd_strerror(n)); if (n == -EPIPE) { pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); @@ -144,14 +167,17 @@ static int mmap_write(struct userdata *u) { return -1; } -/* pa_log("Got request for %i samples", (int) n); */ + /* We only use part of the buffer that matches our + * dynamically requested latency */ - if (n <= 0) + if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) return work_done; - frames = n; + frames = n = n - u->hwbuf_unused_frames; + + if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { - if ((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0) { + pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err)); if (err == -EPIPE) { pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); @@ -188,7 +214,9 @@ static int mmap_write(struct userdata *u) { * a little bit longer around? */ pa_memblock_unref_fixed(chunk.memblock); - if ((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0) { + if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { + + pa_log_debug("snd_pcm_mmap_commit: %s", snd_strerror(err)); if (err == -EPIPE) { pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); @@ -207,10 +235,12 @@ static int mmap_write(struct userdata *u) { work_done = 1; - if (frames >= (snd_pcm_uframes_t) n) - return work_done; + u->frame_index += frames; -/* pa_log("wrote %i samples", (int) frames); */ + pa_log_debug("wrote %llu frames", (unsigned long long) frames); + + if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) + return work_done; } } @@ -225,54 +255,60 @@ static int unix_write(struct userdata *u) { for (;;) { void *p; - snd_pcm_sframes_t t; - ssize_t l; + snd_pcm_sframes_t n, frames; int err; - if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) { + if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { pa_log("Failed to query DSP status data: %s", snd_strerror(err)); return -1; } - if (snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size) + if (PA_UNLIKELY(snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size)) pa_log_debug("Buffer underrun!"); - l = snd_pcm_status_get_avail(status) * u->frame_size; + n = snd_pcm_status_get_avail(status); -/* pa_log("%u bytes to write", l); */ + /* We only use part of the buffer that matches our + * dynamically requested latency */ - if (l <= 0) + if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) return work_done; + n -= u->hwbuf_unused_frames; + if (u->memchunk.length <= 0) - pa_sink_render(u->sink, l, &u->memchunk); + pa_sink_render(u->sink, n * u->frame_size, &u->memchunk); pa_assert(u->memchunk.length > 0); + frames = u->memchunk.length / u->frame_size; + + if (frames > n) + frames = n; + p = pa_memblock_acquire(u->memchunk.memblock); - t = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, u->memchunk.length / u->frame_size); + frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, frames); pa_memblock_release(u->memchunk.memblock); -/* pa_log("wrote %i bytes of %u (%u)", t*u->frame_size, u->memchunk.length, l); */ + pa_assert(frames != 0); - pa_assert(t != 0); + if (PA_UNLIKELY(frames < 0)) { - if (t < 0) { + if (frames == -EPIPE) + pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); - if ((t = snd_pcm_recover(u->pcm_handle, t, 1)) == 0) + if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) continue; - if (t == -EAGAIN) { - pa_log_debug("EAGAIN"); + if (frames == -EAGAIN) return work_done; - } else { - pa_log("Failed to write data to DSP: %s", snd_strerror(t)); - return -1; - } + + pa_log("Failed to write data to DSP: %s", snd_strerror(frames)); + return -1; } - u->memchunk.index += t * u->frame_size; - u->memchunk.length -= t * u->frame_size; + u->memchunk.index += frames * u->frame_size; + u->memchunk.length -= frames * u->frame_size; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); @@ -281,29 +317,51 @@ static int unix_write(struct userdata *u) { work_done = 1; - if (t * u->frame_size >= (unsigned) l) + u->frame_index += frames; + + if (PA_LIKELY(frames >= n)) return work_done; } } -static pa_usec_t sink_get_latency(struct userdata *u) { - pa_usec_t r = 0; - snd_pcm_status_t *status; - snd_pcm_sframes_t frames = 0; +static int update_smoother(struct userdata *u) { + snd_pcm_sframes_t delay; + int64_t frames; int err; - - snd_pcm_status_alloca(&status); + pa_usec_t now1, now2; pa_assert(u); pa_assert(u->pcm_handle); - if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) + /* Let's update the time smoother */ + snd_pcm_avail_update(u->pcm_handle); + + if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { pa_log("Failed to get delay: %s", snd_strerror(err)); - else - frames = snd_pcm_status_get_delay(status); + return -1; + } + + frames = u->frame_index - delay; + + pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); - if (frames > 0) - r = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec); + now1 = pa_rtclock_usec(); + now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec); + pa_smoother_put(u->smoother, now1, now2); + + return 0; +} + +static pa_usec_t sink_get_latency(struct userdata *u) { + pa_usec_t r = 0; + int64_t delay; + + pa_assert(u); + + delay = u->frame_index - pa_smoother_get(u->smoother, pa_rtclock_usec()); + + if (delay > 0) + r = pa_bytes_to_usec(delay * u->frame_size, &u->sink->sample_spec); if (u->memchunk.memblock) r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec); @@ -342,6 +400,8 @@ static int suspend(struct userdata *u) { pa_assert(u); pa_assert(u->pcm_handle); + pa_smoother_pause(u->smoother, pa_rtclock_usec()); + /* Let's suspend */ snd_pcm_drain(u->pcm_handle); snd_pcm_close(u->pcm_handle); @@ -357,10 +417,61 @@ static int suspend(struct userdata *u) { return 0; } +static pa_usec_t hw_sleep_time(struct userdata *u) { + pa_usec_t usec, wm; + + pa_assert(u); + + usec = pa_sink_get_requested_latency(u->sink); + + if (usec <= 0) + usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec); + + pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); + + wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec); + + if (usec >= wm) + usec -= wm; + else + usec /= 2; + + pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); + + return usec; +} + +static int update_sw_params(struct userdata *u) { + size_t avail_min; + int err; + + pa_assert(u); + + if (u->use_tsched) { + pa_usec_t usec; + + usec = hw_sleep_time(u); + + avail_min = pa_usec_to_bytes(usec, &u->sink->sample_spec); + + if (avail_min <= 0) + avail_min = 1; + + } else + avail_min = 1; + + if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { + pa_log("Failed to set software parameters: %s", snd_strerror(err)); + return err; + } + + return 0; +} + static int unsuspend(struct userdata *u) { pa_sample_spec ss; int err; - pa_bool_t b; + pa_bool_t b, d; unsigned nfrags; snd_pcm_uframes_t period_size; @@ -379,13 +490,14 @@ static int unsuspend(struct userdata *u) { nfrags = u->nfragments; period_size = u->fragment_size / u->frame_size; b = u->use_mmap; + d = u->use_tsched; - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b, TRUE)) < 0) { + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { pa_log("Failed to set hardware parameters: %s", snd_strerror(err)); goto fail; } - if (b != u->use_mmap) { + if (b != u->use_mmap || d != u->use_tsched) { pa_log_warn("Resume failed, couldn't get original access mode."); goto fail; } @@ -400,10 +512,8 @@ static int unsuspend(struct userdata *u) { goto fail; } - if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) { - pa_log("Failed to set software parameters: %s", snd_strerror(err)); + if (update_sw_params(u) < 0) goto fail; - } if (build_pollfd(u) < 0) goto fail; @@ -425,6 +535,29 @@ fail: return -1; } +static void update_hwbuf_unused_frames(struct userdata *u) { + pa_usec_t usec; + size_t b; + + pa_assert(u); + + if ((usec = pa_sink_get_requested_latency(u->sink)) <= 0) { + /* Use the full buffer if noone asked us for anything + * specific */ + u->hwbuf_unused_frames = 0; + return; + } + + b = pa_usec_to_bytes(usec, &u->sink->sample_spec); + + /* We need at least one sample in our buffer */ + + if (PA_UNLIKELY(b < u->frame_size)) + b = u->frame_size; + + u->hwbuf_unused_frames = PA_LIKELY(b < u->hwbuf_size) ? ((u->hwbuf_size - b) / u->frame_size) : 0; +} + static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; @@ -474,6 +607,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse } break; + + case PA_SINK_MESSAGE_ADD_INPUT: + case PA_SINK_MESSAGE_REMOVE_INPUT: + case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: { + int r = pa_sink_process_msg(o, code, data, offset, chunk); + update_hwbuf_unused_frames(u); + return r; + } } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -505,18 +646,24 @@ static int sink_get_volume_cb(pa_sink *s) { pa_assert(u->mixer_elem); for (i = 0; i < s->sample_spec.channels; i++) { - long set_vol, vol; + long alsa_vol; pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i])); - if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &vol)) < 0) - goto fail; + if (u->hw_dB_supported) { + + if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) { + s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); + continue; + } + + u->hw_dB_supported = FALSE; + } - set_vol = (long) roundf(((float) s->volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; + if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; - /* Try to avoid superfluous volume changes */ - if (set_vol != vol) - s->volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); + s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); } return 0; @@ -524,8 +671,6 @@ static int sink_get_volume_cb(pa_sink *s) { fail: pa_log_error("Unable to read volume: %s", snd_strerror(err)); - s->get_volume = NULL; - s->set_volume = NULL; return -1; } @@ -543,15 +688,31 @@ static int sink_set_volume_cb(pa_sink *s) { pa_assert(snd_mixer_selem_has_playback_channel(u->mixer_elem, u->mixer_map[i])); - vol = s->volume.values[i]; + vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM); + + if (u->hw_dB_supported) { + alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); + + if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) { - if (vol > PA_VOLUME_NORM) - vol = PA_VOLUME_NORM; + if (snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) + s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); + + continue; + } + + u->hw_dB_supported = FALSE; + } alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) goto fail; + + if (snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) + s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); } return 0; @@ -559,8 +720,6 @@ static int sink_set_volume_cb(pa_sink *s) { fail: pa_log_error("Unable to set volume: %s", snd_strerror(err)); - s->get_volume = NULL; - s->set_volume = NULL; return -1; } @@ -573,9 +732,6 @@ static int sink_get_mute_cb(pa_sink *s) { if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) { pa_log_error("Unable to get switch: %s", snd_strerror(err)); - - s->get_mute = NULL; - s->set_mute = NULL; return -1; } @@ -593,15 +749,20 @@ static int sink_set_mute_cb(pa_sink *s) { if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) { pa_log_error("Unable to set switch: %s", snd_strerror(err)); - - s->get_mute = NULL; - s->set_mute = NULL; return -1; } return 0; } +static void sink_update_requested_latency_cb(pa_sink *s) { + struct userdata *u = s->userdata; + + pa_assert(u); + + update_sw_params(u); +} + static void thread_func(void *userdata) { struct userdata *u = userdata; @@ -615,13 +776,50 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); + update_hwbuf_unused_frames(u); + for (;;) { int ret; + pa_log_debug("loop"); + /* Render some data and write it to the dsp */ if (PA_SINK_OPENED(u->sink->thread_info.state)) { int work_done = 0; + if (u->sink->thread_info.rewind_nbytes > 0 && u->use_tsched) { + snd_pcm_sframes_t frames, limit, unused; + + pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) u->sink->thread_info.rewind_nbytes); + + frames = u->sink->thread_info.rewind_nbytes / u->frame_size; + + if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) { + pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused)); + goto fail; + } + + limit = (u->hwbuf_size / u->frame_size) - unused; + + if (frames > limit) + frames = limit; + + pa_log_debug("Limited to %lu bytes.", (unsigned long) frames * u->frame_size); + + if ((frames = snd_pcm_rewind(u->pcm_handle, frames)) < 0) { + pa_log("snd_pcm_rewind() failed: %s", snd_strerror(frames)); + goto fail; + } + + if ((u->sink->thread_info.rewind_nbytes = frames * u->frame_size) <= 0) + pa_log_info("Tried rewind, but was apparently not possible."); + else { + u->frame_index -= frames; + pa_log_debug("Rewound %lu bytes.", (unsigned long) u->sink->thread_info.rewind_nbytes); + pa_sink_process_rewind(u->sink); + } + } + if (u->use_mmap) { if ((work_done = mmap_write(u)) < 0) goto fail; @@ -630,12 +828,46 @@ static void thread_func(void *userdata) { goto fail; } - if (work_done && u->first) { - pa_log_info("Starting playback."); - snd_pcm_start(u->pcm_handle); - u->first = FALSE; - continue; + pa_log_debug("work_done = %i", work_done); + + if (work_done) { + + if (u->first) { + pa_log_info("Starting playback."); + snd_pcm_start(u->pcm_handle); + u->first = FALSE; + + pa_smoother_resume(u->smoother, pa_rtclock_usec()); + } + + if (update_smoother(u) < 0) + goto fail; } + + if (u->use_tsched) { + pa_usec_t usec, cusec; + + /* OK, the playback buffer is now full, let's + * calculate when to wake up next */ + + usec = hw_sleep_time(u); + + pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) usec / PA_USEC_PER_MSEC); + + /* Convert from the sound card time domain to the + * system time domain */ + cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), usec); + + pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); + + /* We don't trust the conversion, so we wake up whatever comes first */ + pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); + } + + } else if (u->use_tsched) { + + /* OK, we're in an invalid state, let's disable our timers */ + pa_rtpoll_set_timer_disabled(u->rtpoll); } /* Hmm, nothing to do. Let's sleep */ @@ -697,6 +929,8 @@ static void thread_func(void *userdata) { break; } } + + pa_log_debug("alsa revents = %i", revents); } } @@ -717,16 +951,23 @@ int pa__init(pa_module*m) { const char *dev_id; pa_sample_spec ss; pa_channel_map map; - uint32_t nfrags, frag_size; - snd_pcm_uframes_t period_size; + uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark; + snd_pcm_uframes_t period_frames, tsched_frames; size_t frame_size; snd_pcm_info_t *pcm_info = NULL; int err; - char *t; const char *name; char *name_buf = NULL; - int namereg_fail; - pa_bool_t use_mmap = TRUE, b; + pa_bool_t namereg_fail; + pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d; + pa_usec_t usec; + pa_sink_new_data data; + static const char * const class_table[SND_PCM_CLASS_LAST+1] = { + [SND_PCM_CLASS_GENERIC] = "sound", + [SND_PCM_CLASS_MULTI] = NULL, + [SND_PCM_CLASS_MODEM] = "modem", + [SND_PCM_CLASS_DIGITIZER] = NULL + }; snd_pcm_info_alloca(&pcm_info); @@ -746,35 +987,60 @@ int pa__init(pa_module*m) { frame_size = pa_frame_size(&ss); nfrags = m->core->default_n_fragments; - frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss); + frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss); if (frag_size <= 0) frag_size = frame_size; + tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss); + tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss); - if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0) { + if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || + pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || + pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 || + pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) { pa_log("Failed to parse buffer metrics"); goto fail; } - period_size = frag_size/frame_size; + + hwbuf_size = frag_size * nfrags; + period_frames = frag_size/frame_size; + tsched_frames = tsched_size/frame_size; if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { pa_log("Failed to parse mmap argument."); goto fail; } + if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) { + pa_log("Failed to parse timer_scheduling argument."); + goto fail; + } + + if (use_tsched && !pa_rtclock_hrtimer()) { + pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel."); + use_tsched = FALSE; + } + u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; m->userdata = u; u->use_mmap = use_mmap; + u->use_tsched = use_tsched; u->first = TRUE; pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); u->alsa_rtpoll_item = NULL; pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE); + usec = pa_rtclock_usec(); + pa_smoother_set_time_offset(u->smoother, usec); + pa_smoother_pause(u->smoother, usec); + snd_config_update_free_global(); b = use_mmap; + d = use_tsched; if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { @@ -783,8 +1049,8 @@ int pa__init(pa_module*m) { &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, - &nfrags, &period_size, - &b))) + &nfrags, &period_frames, tsched_frames, + &b, &d))) goto fail; @@ -795,8 +1061,8 @@ int pa__init(pa_module*m) { &u->device_name, &ss, &map, SND_PCM_STREAM_PLAYBACK, - &nfrags, &period_size, - &b))) + &nfrags, &period_frames, tsched_frames, + &b, &d))) goto fail; } @@ -806,22 +1072,25 @@ int pa__init(pa_module*m) { if (use_mmap && !b) { pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode."); - u->use_mmap = use_mmap = b; + u->use_mmap = use_mmap = FALSE; + } + + if (use_tsched && (!b || !d)) { + pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling."); + u->use_tsched = use_tsched = FALSE; } if (u->use_mmap) pa_log_info("Successfully enabled mmap() mode."); + if (u->use_tsched) + pa_log_info("Successfully enabled timer-based scheduling mode."); + if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) { pa_log("Error fetching PCM info: %s", snd_strerror(err)); goto fail; } - if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) { - pa_log("Failed to set software parameters: %s", snd_strerror(err)); - goto fail; - } - /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); @@ -853,13 +1122,31 @@ int pa__init(pa_module*m) { } if ((name = pa_modargs_get_value(ma, "sink_name", NULL))) - namereg_fail = 1; + namereg_fail = TRUE; else { name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name); - namereg_fail = 0; + namereg_fail = FALSE; } - u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map); + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, name); + data.namereg_fail = namereg_fail; + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "alsa"); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, snd_pcm_info_get_name(pcm_info)); + + if (class_table[snd_pcm_info_get_class(pcm_info)]) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, class_table[snd_pcm_info_get_class(pcm_info)]); + + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap_rewrite" : (u->use_mmap ? "mmap" : "serial")); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); + pa_sink_new_data_done(&data); pa_xfree(name_buf); if (!u->sink) { @@ -868,26 +1155,35 @@ int pa__init(pa_module*m) { } u->sink->parent.process_msg = sink_process_msg; + u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->userdata = u; - pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - pa_sink_set_description(u->sink, t = pa_sprintf_malloc( - "ALSA PCM on %s (%s)%s", - u->device_name, - snd_pcm_info_get_name(pcm_info), - use_mmap ? " via DMA" : "")); - pa_xfree(t); - - u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY; u->frame_size = frame_size; - u->fragment_size = frag_size = period_size * frame_size; + u->fragment_size = frag_size = period_frames * frame_size; u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; + u->hwbuf_unused_frames = 0; + u->tsched_watermark = tsched_watermark; + u->frame_index = 0; + u->hw_dB_supported = FALSE; + u->hw_dB_min = u->hw_dB_max = 0; + u->hw_volume_min = u->hw_volume_max = 0; + + u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0; + + pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", + nfrags, (long unsigned) u->fragment_size, + (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); - pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size); + if (use_tsched) + pa_log_info("Time scheduling watermark is %0.2fms", + (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC); + + if (update_sw_params(u) < 0) + goto fail; pa_memchunk_reset(&u->memchunk); @@ -895,17 +1191,71 @@ int pa__init(pa_module*m) { pa_assert(u->mixer_elem); if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) - if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0) { - u->sink->get_volume = sink_get_volume_cb; - u->sink->set_volume = sink_set_volume_cb; - snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max); - u->sink->flags |= PA_SINK_HW_VOLUME_CTRL; + + if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0 && + snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) { + + pa_bool_t suitable = TRUE; + + pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max); + + if (u->hw_volume_min > u->hw_volume_max) { + + pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max); + suitable = FALSE; + + } else if (u->hw_volume_max - u->hw_volume_min < 3) { + + pa_log_info("Device has less than 4 volume levels. Falling back to software volume control."); + suitable = FALSE; + + } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { + + pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); + + /* Let's see if this thing actually is useful for muting */ + if (u->hw_dB_min > -6000) { + pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100); + + suitable = FALSE; + } else if (u->hw_dB_max < 0) { + + pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100); + suitable = FALSE; + + } else if (u->hw_dB_min >= u->hw_dB_max) { + + pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100); + suitable = FALSE; + + } else { + + if (u->hw_dB_max > 0) { + /* dB > 0 means overamplification, and clipping, we don't want that here */ + pa_log_info("Device can do overamplification for %0.2f dB. Limiting to 0 db", ((double) u->hw_dB_max) / 100); + u->hw_dB_max = 0; + } + + u->hw_dB_supported = TRUE; + } + } + + if (suitable) { + u->sink->get_volume = sink_get_volume_cb; + u->sink->set_volume = sink_set_volume_cb; + u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0); + pa_log_info("Using hardware volume control. %s dB scale.", u->hw_dB_supported ? "Using" : "Not using"); + + } else { + pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); + pa_alsa_0dB_playback(u->mixer_elem); + } } if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) { u->sink->get_mute = sink_get_mute_cb; u->sink->set_mute = sink_set_mute_cb; - u->sink->flags |= PA_SINK_HW_VOLUME_CTRL; + u->sink->flags |= PA_SINK_HW_MUTE_CTRL; } u->mixer_fdl = pa_alsa_fdlist_new(); @@ -926,10 +1276,21 @@ int pa__init(pa_module*m) { } /* Get initial mixer settings */ - if (u->sink->get_volume) - u->sink->get_volume(u->sink); - if (u->sink->get_mute) - u->sink->get_mute(u->sink); + if (data.volume_is_set) { + if (u->sink->set_volume) + u->sink->set_volume(u->sink); + } else { + if (u->sink->get_volume) + u->sink->get_volume(u->sink); + } + + if (data.muted_is_set) { + if (u->sink->set_mute) + u->sink->set_mute(u->sink); + } else { + if (u->sink->get_mute) + u->sink->get_mute(u->sink); + } pa_sink_put(u->sink); @@ -988,6 +1349,9 @@ void pa__done(pa_module*m) { snd_pcm_close(u->pcm_handle); } + if (u->smoother) + pa_smoother_free(u->smoother); + pa_xfree(u->device_name); pa_xfree(u); diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 23a2f921..b2d0d43d 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -47,6 +48,8 @@ #include #include #include +#include +#include #include "alsa-util.h" #include "module-alsa-source-symdef.h" @@ -60,14 +63,19 @@ PA_MODULE_USAGE( "device= " "device_id= " "format= " - "channels= " "rate= " + "channels= " + "channel_map= " "fragments= " "fragment_size= " - "channel_map= " - "mmap="); + "mmap= " + "tsched= " + "tsched_buffer_size= " + "tsched_buffer_watermark="); #define DEFAULT_DEVICE "default" +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) +#define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC) struct userdata { pa_core *core; @@ -85,29 +93,35 @@ struct userdata { snd_mixer_elem_t *mixer_elem; long hw_volume_max, hw_volume_min; - size_t frame_size, fragment_size, hwbuf_size; + size_t frame_size, fragment_size, hwbuf_size, tsched_watermark; unsigned nfragments; char *device_name; - pa_bool_t use_mmap; + pa_bool_t use_mmap, use_tsched; pa_rtpoll_item *alsa_rtpoll_item; snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST]; + + pa_smoother *smoother; + int64_t frame_index; }; static const char* const valid_modargs[] = { + "source_name", "device", "device_id", - "source_name", - "channels", - "rate", "format", + "rate", + "channels", + "channel_map", "fragments", "fragment_size", - "channel_map", "mmap", + "tsched", + "tsched_buffer_size", + "tsched_buffer_watermark", NULL }; @@ -125,7 +139,7 @@ static int mmap_read(struct userdata *u) { pa_memchunk chunk; void *p; - if ((n = snd_pcm_avail_update(u->pcm_handle)) < 0) { + if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { if (n == -EPIPE) pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); @@ -140,14 +154,12 @@ static int mmap_read(struct userdata *u) { return -1; } -/* pa_log("Got request for %i samples", (int) n); */ - - if (n <= 0) + if (PA_UNLIKELY(n <= 0)) return work_done; frames = n; - if ((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0) { + if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { if (err == -EPIPE) pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); @@ -182,7 +194,7 @@ static int mmap_read(struct userdata *u) { * a little bit longer around? */ pa_memblock_unref_fixed(chunk.memblock); - if ((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0) { + if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { if (err == -EPIPE) pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); @@ -199,7 +211,10 @@ static int mmap_read(struct userdata *u) { work_done = 1; -/* pa_log("wrote %i samples", (int) frames); */ + u->frame_index += frames; + + if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) + return work_done; } } @@ -214,87 +229,98 @@ static int unix_read(struct userdata *u) { for (;;) { void *p; - snd_pcm_sframes_t t, k; - ssize_t l; + snd_pcm_sframes_t n, frames; int err; pa_memchunk chunk; - if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) { + if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { pa_log("Failed to query DSP status data: %s", snd_strerror(err)); return -1; } - if (snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size) + if (PA_UNLIKELY(snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size)) pa_log_debug("Buffer overrun!"); - l = snd_pcm_status_get_avail(status) * u->frame_size; + n = snd_pcm_status_get_avail(status); - if (l <= 0) + if (PA_UNLIKELY(n <= 0)) return work_done; chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); - k = pa_memblock_get_length(chunk.memblock); - - if (k > l) - k = l; + frames = pa_memblock_get_length(chunk.memblock) / u->frame_size; - k = (k/u->frame_size)*u->frame_size; + if (frames > n) + frames = n; p = pa_memblock_acquire(chunk.memblock); - t = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, k / u->frame_size); + frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames); pa_memblock_release(chunk.memblock); -/* pa_log("wrote %i bytes of %u (%u)", t*u->frame_size, u->memchunk.length, l); */ + pa_assert(frames != 0); - pa_assert(t != 0); - - if (t < 0) { + if (PA_UNLIKELY(frames < 0)) { pa_memblock_unref(chunk.memblock); - if ((t = snd_pcm_recover(u->pcm_handle, t, 1)) == 0) + if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) continue; - if (t == -EAGAIN) { - pa_log_debug("EAGAIN"); + if (frames == -EAGAIN) return work_done; - } else { - pa_log("Failed to read data from DSP: %s", snd_strerror(t)); - return -1; - } + + pa_log("Failed to read data from DSP: %s", snd_strerror(frames)); + return -1; } chunk.index = 0; - chunk.length = t * u->frame_size; + chunk.length = frames * u->frame_size; pa_source_post(u->source, &chunk); pa_memblock_unref(chunk.memblock); work_done = 1; - if (t * u->frame_size >= (unsigned) l) + u->frame_index += frames; + + if (PA_LIKELY(frames >= n)) return work_done; } } -static pa_usec_t source_get_latency(struct userdata *u) { - pa_usec_t r = 0; +static int update_smoother(struct userdata *u) { snd_pcm_status_t *status; - snd_pcm_sframes_t frames = 0; + int64_t frames; int err; - snd_pcm_status_alloca(&status); - pa_assert(u); pa_assert(u->pcm_handle); - if ((err = snd_pcm_status(u->pcm_handle, status)) < 0) + snd_pcm_status_alloca(&status); + + /* Let's update the time smoother */ + + if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { pa_log("Failed to get delay: %s", snd_strerror(err)); - else - frames = snd_pcm_status_get_delay(status); + return -1; + } + + frames = u->frame_index + snd_pcm_status_get_delay(status); + + pa_smoother_put(u->smoother, pa_rtclock_usec(), pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec)); + + return 0; +} + +static pa_usec_t source_get_latency(struct userdata *u) { + pa_usec_t r = 0; + int64_t delay; + + pa_assert(u); - if (frames > 0) - r = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec); + delay = pa_smoother_get(u->smoother, pa_rtclock_usec()) - u->frame_index; + + if (delay > 0) + r = pa_bytes_to_usec(delay * u->frame_size, &u->source->sample_spec); return r; } @@ -330,6 +356,8 @@ static int suspend(struct userdata *u) { pa_assert(u); pa_assert(u->pcm_handle); + pa_smoother_pause(u->smoother, pa_rtclock_usec()); + /* Let's suspend */ snd_pcm_close(u->pcm_handle); u->pcm_handle = NULL; @@ -344,10 +372,55 @@ static int suspend(struct userdata *u) { return 0; } +static pa_usec_t hw_sleep_time(struct userdata *u) { + pa_usec_t usec; + + pa_assert(u); + + usec = pa_source_get_requested_latency(u->source); + + if (usec <= 0) + usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); + + if (usec >= u->tsched_watermark) + usec -= u->tsched_watermark; + else + usec /= 2; + + return usec; +} + +static int update_sw_params(struct userdata *u) { + size_t avail_min; + int err; + + pa_assert(u); + + if (u->use_tsched) { + pa_usec_t usec; + + usec = hw_sleep_time(u); + + avail_min = pa_usec_to_bytes(usec, &u->source->sample_spec); + + if (avail_min <= 0) + avail_min = 1; + + } else + avail_min = 1; + + if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { + pa_log("Failed to set software parameters: %s", snd_strerror(err)); + return err; + } + + return 0; +} + static int unsuspend(struct userdata *u) { pa_sample_spec ss; int err; - pa_bool_t b; + pa_bool_t b, d; unsigned nfrags; snd_pcm_uframes_t period_size; @@ -366,13 +439,14 @@ static int unsuspend(struct userdata *u) { nfrags = u->nfragments; period_size = u->fragment_size / u->frame_size; b = u->use_mmap; + d = u->use_tsched; - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, &b, TRUE)) < 0) { + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { pa_log("Failed to set hardware parameters: %s", snd_strerror(err)); goto fail; } - if (b != u->use_mmap) { + if (b != u->use_mmap || d != u->use_tsched) { pa_log_warn("Resume failed, couldn't get original access mode."); goto fail; } @@ -387,17 +461,17 @@ static int unsuspend(struct userdata *u) { goto fail; } - if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) { - pa_log("Failed to set software parameters: %s", snd_strerror(err)); + if (update_sw_params(u) < 0) goto fail; - } if (build_pollfd(u) < 0) goto fail; + /* FIXME: We need to reload the volume somehow */ + snd_pcm_start(u->pcm_handle); - /* FIXME: We need to reload the volume somehow */ + pa_smoother_resume(u->smoother, pa_rtclock_usec()); pa_log_info("Resumed successfully..."); @@ -609,15 +683,39 @@ static void thread_func(void *userdata) { /* Read some data and pass it to the sources */ if (PA_SOURCE_OPENED(u->source->thread_info.state)) { + int work_done = 0; if (u->use_mmap) { - if (mmap_read(u) < 0) + if ((work_done = mmap_read(u)) < 0) goto fail; } else { - if (unix_read(u) < 0) + if ((work_done = unix_read(u) < 0)) goto fail; } + + if (update_smoother(u) < 0) + goto fail; + + if (u->use_tsched && work_done) { + pa_usec_t usec, cusec; + + /* OK, the capture buffer is now empty, let's + * calculate when to wake up next */ + + usec = hw_sleep_time(u); + + pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) usec / PA_USEC_PER_MSEC); + + /* Convert from the sound card time domain to the + * system time domain */ + cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), usec); + + pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); + + /* We don't trust the conversion, so we wake up whatever comes first */ + pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); + } } /* Hmm, nothing to do. Let's sleep */ @@ -699,16 +797,22 @@ int pa__init(pa_module*m) { const char *dev_id; pa_sample_spec ss; pa_channel_map map; - uint32_t nfrags, frag_size; - snd_pcm_uframes_t period_size; + uint32_t nfrags, frag_size, tsched_size, tsched_watermark; + snd_pcm_uframes_t period_frames, tsched_frames; size_t frame_size; snd_pcm_info_t *pcm_info = NULL; int err; - char *t; const char *name; char *name_buf = NULL; - int namereg_fail; - pa_bool_t use_mmap = TRUE, b; + pa_bool_t namereg_fail; + pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d; + pa_source_new_data data; + static const char * const class_table[SND_PCM_CLASS_LAST+1] = { + [SND_PCM_CLASS_GENERIC] = "sound", + [SND_PCM_CLASS_MULTI] = NULL, + [SND_PCM_CLASS_MODEM] = "modem", + [SND_PCM_CLASS_DIGITIZER] = NULL + }; snd_pcm_info_alloca(&pcm_info); @@ -728,34 +832,55 @@ int pa__init(pa_module*m) { frame_size = pa_frame_size(&ss); nfrags = m->core->default_n_fragments; - frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*1000, &ss); + frag_size = pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss); if (frag_size <= 0) frag_size = frame_size; + tsched_size = pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss); + tsched_watermark = pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss); - if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0) { + if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 || + pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 || + pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 || + pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) { pa_log("Failed to parse buffer metrics"); goto fail; } - period_size = frag_size/frame_size; + + period_frames = frag_size/frame_size; + tsched_frames = tsched_size/frame_size; if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { pa_log("Failed to parse mmap argument."); goto fail; } + if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) { + pa_log("Failed to parse timer_scheduling argument."); + goto fail; + } + + if (use_tsched && !pa_rtclock_hrtimer()) { + pa_log("Disabling timer-based scheduling because high-resolution timers are not available from the kernel."); + use_tsched = FALSE; + } + u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; m->userdata = u; u->use_mmap = use_mmap; + u->use_tsched = use_tsched; pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); u->alsa_rtpoll_item = NULL; pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE); + pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); snd_config_update_free_global(); b = use_mmap; + d = use_tsched; if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) { @@ -764,8 +889,8 @@ int pa__init(pa_module*m) { &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_size, - &b))) + &nfrags, &period_frames, tsched_frames, + &b, &d))) goto fail; } else { @@ -775,8 +900,8 @@ int pa__init(pa_module*m) { &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_size, - &b))) + &nfrags, &period_frames, tsched_frames, + &b, &d))) goto fail; } @@ -788,18 +913,24 @@ int pa__init(pa_module*m) { u->use_mmap = use_mmap = b; } + if (use_tsched && (!b || !d)) { + pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling."); + u->use_tsched = use_tsched = FALSE; + } + if (u->use_mmap) pa_log_info("Successfully enabled mmap() mode."); + if (u->use_tsched) + pa_log_info("Successfully enabled timer-based scheduling mode."); + if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) { pa_log("Error fetching PCM info: %s", snd_strerror(err)); goto fail; } - if ((err = pa_alsa_set_sw_params(u->pcm_handle)) < 0) { - pa_log("Failed to set software parameters: %s", snd_strerror(err)); + if (update_sw_params(u) < 0) goto fail; - } /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); @@ -832,13 +963,31 @@ int pa__init(pa_module*m) { } if ((name = pa_modargs_get_value(ma, "source_name", NULL))) - namereg_fail = 1; + namereg_fail = TRUE; else { name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name); - namereg_fail = 0; + namereg_fail = FALSE; } - u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map); + pa_source_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_source_new_data_set_name(&data, name); + data.namereg_fail = namereg_fail; + pa_source_new_data_set_sample_spec(&data, &ss); + pa_source_new_data_set_channel_map(&data, &map); + + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "alsa"); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, snd_pcm_info_get_name(pcm_info)); + + if (class_table[snd_pcm_info_get_class(pcm_info)]) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, class_table[snd_pcm_info_get_class(pcm_info)]); + + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap_rewrite" : (u->use_mmap ? "mmap" : "serial")); + + u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); + pa_source_new_data_done(&data); pa_xfree(name_buf); if (!u->source) { @@ -849,22 +998,15 @@ int pa__init(pa_module*m) { u->source->parent.process_msg = source_process_msg; u->source->userdata = u; - pa_source_set_module(u->source, m); pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); - pa_source_set_description(u->source, t = pa_sprintf_malloc( - "ALSA PCM on %s (%s)%s", - u->device_name, - snd_pcm_info_get_name(pcm_info), - use_mmap ? " via DMA" : "")); - pa_xfree(t); - - u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY; u->frame_size = frame_size; - u->fragment_size = frag_size = period_size * frame_size; + u->fragment_size = frag_size = period_frames * frame_size; u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; + u->tsched_watermark = tsched_watermark; + u->frame_index = 0; pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size); @@ -961,6 +1103,9 @@ void pa__done(pa_module*m) { snd_pcm_close(u->pcm_handle); } + if (u->smoother) + pa_smoother_free(u->smoother); + pa_xfree(u->device_name); pa_xfree(u); diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 996cd4f6..0b17bc5a 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -66,7 +66,7 @@ PA_MODULE_USAGE( "channel_map="); #define DEFAULT_SINK_NAME "combined" -#define MEMBLOCKQ_MAXLENGTH (1024*170) +#define MEMBLOCKQ_MAXLENGTH (1024*1024*16) #define DEFAULT_ADJUST_TIME 10 @@ -139,7 +139,7 @@ enum { }; enum { - SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX + SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX, }; static void output_free(struct output *o); @@ -203,10 +203,10 @@ static void adjust_rates(struct userdata *u) { r += (uint32_t) (((((double) o->total_latency - target_latency))/u->adjust_time)*r/PA_USEC_PER_SEC); if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) { - pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->name, base_rate, r); + pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r); pa_sink_input_set_rate(o->sink_input, base_rate); } else { - pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency); + pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), r, (double) r / base_rate, (float) o->total_latency); pa_sink_input_set_rate(o->sink_input, r); } } @@ -250,6 +250,10 @@ static void thread_func(void *userdata) { if (u->sink->thread_info.state == PA_SINK_RUNNING && !u->thread_info.active_outputs) { struct timeval now; + /* Just rewind if necessary, since we are in NULL mode, we + * don't have to pass this on */ + pa_sink_process_rewind(u->sink); + pa_rtclock_get(&now); if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) { @@ -354,27 +358,20 @@ static void request_memblock(struct output *o, size_t length) { } /* Called from I/O thread context */ -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct output *o; pa_sink_input_assert_ref(i); pa_assert_se(o = i->userdata); /* If necessary, get some new data */ - request_memblock(o, length); - - return pa_memblockq_peek(o->memblockq, chunk); -} - -/* Called from I/O thread context */ -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { - struct output *o; + request_memblock(o, nbytes); - pa_sink_input_assert_ref(i); - pa_assert(length > 0); - pa_assert_se(o = i->userdata); + if (pa_memblockq_peek(o->memblockq, chunk) < 0) + return -1; - pa_memblockq_drop(o->memblockq, length); + pa_memblockq_drop(o->memblockq, chunk->length); + return 0; } /* Called from I/O thread context */ @@ -440,6 +437,7 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64 pa_memblockq_flush(o->memblockq); break; + } return pa_sink_input_process_msg(obj, code, data, offset, chunk); @@ -665,10 +663,10 @@ static void update_description(struct userdata *u) { char *e; if (first) { - e = pa_sprintf_malloc("%s %s", t, o->sink->description); + e = pa_sprintf_malloc("%s %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); first = 0; } else - e = pa_sprintf_malloc("%s, %s", t, o->sink->description); + e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); pa_xfree(t); t = e; @@ -723,12 +721,12 @@ static int output_create_sink_input(struct output *o) { if (o->sink_input) return 0; - t = pa_sprintf_malloc("Simultaneous output on %s", o->sink->description); + t = pa_sprintf_malloc("Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); pa_sink_input_new_data_init(&data); data.sink = o->sink; data.driver = __FILE__; - data.name = t; + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t); pa_sink_input_new_data_set_sample_spec(&data, &o->userdata->sink->sample_spec); pa_sink_input_new_data_set_channel_map(&data, &o->userdata->sink->channel_map); data.module = o->userdata->module; @@ -736,14 +734,15 @@ static int output_create_sink_input(struct output *o) { o->sink_input = pa_sink_input_new(o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new_data_done(&data); + pa_xfree(t); if (!o->sink_input) return -1; o->sink_input->parent.process_msg = sink_input_process_msg; - o->sink_input->peek = sink_input_peek_cb; - o->sink_input->drop = sink_input_drop_cb; + o->sink_input->pop = sink_input_pop_cb; o->sink_input->attach = sink_input_attach_cb; o->sink_input->detach = sink_input_detach_cb; o->sink_input->kill = sink_input_kill_cb; @@ -775,6 +774,7 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { pa_frame_size(&u->sink->sample_spec), 1, 0, + 0, NULL); pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0); @@ -920,6 +920,7 @@ int pa__init(pa_module*m) { pa_channel_map map; struct output *o; uint32_t idx; + pa_sink_new_data data; pa_assert(m); @@ -1003,7 +1004,19 @@ int pa__init(pa_module*m) { goto fail; } - if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { + pa_sink_new_data_init(&data); + data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + data.namereg_fail = FALSE; + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output"); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); + pa_sink_new_data_done(&data); + + if (!u->sink) { pa_log("Failed to create sink"); goto fail; } @@ -1013,9 +1026,6 @@ int pa__init(pa_module*m) { u->sink->set_state = sink_set_state; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY; - pa_sink_set_module(u->sink, m); - pa_sink_set_description(u->sink, "Simultaneous output"); pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); @@ -1075,7 +1085,7 @@ int pa__init(pa_module*m) { } } - u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) sink_new_hook_cb, u); + u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], (pa_hook_cb_t) sink_new_hook_cb, u); } u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) sink_unlink_hook_cb, u); diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index f9bea63d..51c76dcb 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -508,6 +508,7 @@ int pa__init(pa_module*m) { char *t; const char *espeaker; uint32_t key; + pa_sink_new_data data; pa_assert(m); @@ -554,16 +555,23 @@ int pa__init(pa_module*m) { u->state = STATE_AUTH; u->latency = 0; - if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, NULL))) { + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &ss); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK); + pa_sink_new_data_done(&data); + + if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK; - pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c index 832bc73e..44b31a59 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -372,7 +372,7 @@ static int hal_device_add_all(struct userdata *u, const char *capability) { pa_log_debug("Not loaded device %s", udis[i]); else { if (d->sink_name) - pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, PA_VOLUME_NORM, 0); + pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL); count++; } } @@ -412,7 +412,7 @@ static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const s pa_log_debug("Not loaded device %s", td->udi); else { if (d->sink_name) - pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, PA_VOLUME_NORM, 0); + pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL); } } } @@ -575,7 +575,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo if (prev_suspended && !suspend) { /* resume */ if (pa_sink_suspend(sink, 0) >= 0) - pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0); + pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL); else d->acl_race_fix = 1; @@ -643,7 +643,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo if (prev_suspended) { /* resume */ if (pa_sink_suspend(sink, 0) >= 0) - pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, 0); + pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL); } } } diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c index a42aa9ef..80a14a6f 100644 --- a/src/modules/module-jack-sink.c +++ b/src/modules/module-jack-sink.c @@ -277,6 +277,7 @@ int pa__init(pa_module*m) { unsigned i; const char **ports = NULL, **p; char *t; + pa_sink_new_data data; pa_assert(m); @@ -355,20 +356,32 @@ int pa__init(pa_module*m) { } } - if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { - pa_log("failed to create sink."); + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); + if (server_name) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Jack sink (%s)", jack_get_client_name(u->client))); + pa_xfree(t); + pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); + pa_sink_new_data_done(&data); + + if (!u->sink) { + pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY; - pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Jack sink (%s)", jack_get_client_name(u->client))); - pa_xfree(t); jack_set_process_callback(u->client, jack_process, u); jack_on_shutdown(u->client, jack_shutdown, u); diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c index 4ee08bf1..a687563d 100644 --- a/src/modules/module-jack-source.c +++ b/src/modules/module-jack-source.c @@ -254,6 +254,7 @@ int pa__init(pa_module*m) { unsigned i; const char **ports = NULL, **p; char *t; + pa_source_new_data data; pa_assert(m); @@ -326,20 +327,32 @@ int pa__init(pa_module*m) { } } - if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) { - pa_log("failed to create source."); + pa_source_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); + pa_source_new_data_set_sample_spec(&data, &ss); + pa_source_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); + if (server_name) + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Jack source (%s)", jack_get_client_name(u->client))); + pa_xfree(t); + pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); + + u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY); + pa_source_new_data_done(&data); + + if (!u->source) { + pa_log("Failed to create source."); goto fail; } u->source->parent.process_msg = source_process_msg; u->source->userdata = u; - u->source->flags = PA_SOURCE_LATENCY; - pa_source_set_module(u->source, m); pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); - pa_source_set_description(u->source, t = pa_sprintf_malloc("Jack source (%s)", jack_get_client_name(u->client))); - pa_xfree(t); jack_set_process_callback(u->client, jack_process, u); jack_on_shutdown(u->client, jack_shutdown, u); diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index b31037b6..696b6ea6 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -78,8 +78,6 @@ struct userdata { /* This is a dummy buffer. Every port must be connected, but we don't care about control out ports. We connect them all to this single buffer. */ LADSPA_Data control_out; - - pa_memchunk memchunk; }; static const char* const valid_modargs[] = { @@ -107,7 +105,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - *((pa_usec_t*) data) = usec + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec); + *((pa_usec_t*) data) = usec /* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec) */; return 0; } } @@ -128,13 +126,36 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { return 0; } +/* Called from I/O thread context */ +static void sink_request_rewind(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + /* Just hand this one over to the master sink */ + pa_sink_input_request_rewrite(u->sink_input, s->thread_info.rewind_nbytes); +} + +/* Called from I/O thread context */ +static void sink_update_requested_latency(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + /* Just hand this one over to the master sink */ + u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency(s); + pa_sink_invalidate_requested_latency(u->master); +} + /* Called from I/O thread context */ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK_INPUT(o)->userdata; switch (code) { case PA_SINK_INPUT_MESSAGE_GET_LATENCY: - *((pa_usec_t*) data) = pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec); + *((pa_usec_t*) data) = 0 /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec)*/; /* Fall through, the default handler will add in the extra * latency added by the resampler */ @@ -145,87 +166,76 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t } /* Called from I/O thread context */ -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; + float *src, *dst; + size_t fs; + unsigned n, c; + pa_memchunk tchunk; pa_sink_input_assert_ref(i); + pa_assert(chunk); pa_assert_se(u = i->userdata); - if (!u->memchunk.memblock) { - pa_memchunk tchunk; - float *src, *dst; - size_t fs; - unsigned n, c; + pa_sink_render(u->sink, nbytes, &tchunk); - pa_sink_render(u->sink, length, &tchunk); + fs = pa_frame_size(&i->sample_spec); + n = tchunk.length / fs; - fs = pa_frame_size(&i->sample_spec); - n = tchunk.length / fs; + pa_assert(n > 0); - pa_assert(n > 0); + chunk->memblock = pa_memblock_new(i->sink->core->mempool, tchunk.length); + chunk->index = 0; + chunk->length = tchunk.length; - u->memchunk.memblock = pa_memblock_new(i->sink->core->mempool, tchunk.length); - u->memchunk.index = 0; - u->memchunk.length = tchunk.length; + src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); + dst = (float*) pa_memblock_acquire(chunk->memblock); - src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); - dst = (float*) pa_memblock_acquire(u->memchunk.memblock); + for (c = 0; c < u->channels; c++) { + unsigned j; + float *p, *q; - for (c = 0; c < u->channels; c++) { - unsigned j; - float *p, *q; + p = src + c; + q = u->input; + for (j = 0; j < n; j++, p += u->channels, q++) + *q = PA_CLAMP_UNLIKELY(*p, -1.0, 1.0); - p = src + c; - q = u->input; - for (j = 0; j < n; j++, p += u->channels, q++) - *q = PA_CLAMP_UNLIKELY(*p, -1.0, 1.0); + u->descriptor->run(u->handle[c], n); - u->descriptor->run(u->handle[c], n); - - q = u->output; - p = dst + c; - for (j = 0; j < n; j++, q++, p += u->channels) - *p = PA_CLAMP_UNLIKELY(*q, -1.0, 1.0); - } - - pa_memblock_release(tchunk.memblock); - pa_memblock_release(u->memchunk.memblock); - - pa_memblock_unref(tchunk.memblock); + q = u->output; + p = dst + c; + for (j = 0; j < n; j++, q++, p += u->channels) + *p = PA_CLAMP_UNLIKELY(*q, -1.0, 1.0); } - pa_assert(u->memchunk.length > 0); - pa_assert(u->memchunk.memblock); + pa_memblock_release(tchunk.memblock); + pa_memblock_release(chunk->memblock); - *chunk = u->memchunk; - pa_memblock_ref(chunk->memblock); + pa_memblock_unref(tchunk.memblock); return 0; } /* Called from I/O thread context */ -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_assert(length > 0); + pa_assert(nbytes > 0); - if (u->memchunk.memblock) { + u->sink->thread_info.rewind_nbytes = nbytes; + pa_sink_process_rewind(u->sink); +} - if (length < u->memchunk.length) { - u->memchunk.index += length; - u->memchunk.length -= length; - return; - } +/* Called from I/O thread context */ +static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; - pa_memblock_unref(u->memchunk.memblock); - length -= u->memchunk.length; - pa_memchunk_reset(&u->memchunk); - } + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); - if (length > 0) - pa_sink_skip(u->sink, length); + pa_sink_set_max_rewind(u->sink, nbytes); } /* Called from I/O thread context */ @@ -275,8 +285,10 @@ int pa__init(pa_module*m) { pa_channel_map map; pa_modargs *ma; char *t; + const char *z; pa_sink *master; - pa_sink_input_new_data data; + pa_sink_input_new_data sink_input_data; + pa_sink_new_data sink_data; const char *plugin, *label; LADSPA_Descriptor_Function descriptor_func; const char *e, *cdata; @@ -284,7 +296,6 @@ int pa__init(pa_module*m) { unsigned long input_port, output_port, p, j, n_control; unsigned c; pa_bool_t *use_default = NULL; - char *default_sink_name = NULL; pa_assert(m); @@ -325,7 +336,8 @@ int pa__init(pa_module*m) { u->module = m; m->userdata = u; u->master = master; - pa_memchunk_reset(&u->memchunk); + u->sink = NULL; + u->sink_input = NULL; if (!(e = getenv("LADSPA_PATH"))) e = LADSPA_PATH; @@ -583,40 +595,65 @@ int pa__init(pa_module*m) { for (c = 0; c < u->channels; c++) d->activate(u->handle[c]); - default_sink_name = pa_sprintf_malloc("%s.ladspa", master->name); - /* Create sink */ - if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", default_sink_name), 0, &ss, &map))) { + pa_sink_new_data_init(&sink_data); + sink_data.driver = __FILE__; + sink_data.module = m; + if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) + sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name); + sink_data.namereg_fail = FALSE; + pa_sink_new_data_set_sample_spec(&sink_data, &ss); + pa_sink_new_data_set_channel_map(&sink_data, &map); + z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("LADSPA Plugin %s on %s", label, z ? z : master->name)); + pa_xfree(t); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); + pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin); + pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label); + pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name); + pa_proplist_sets(sink_data.proplist, "device.ladspa.maker", d->Maker); + pa_proplist_sets(sink_data.proplist, "device.ladspa.copyright", d->Copyright); + pa_proplist_sets(sink_data.proplist, "device.ladspa.unique_id", t = pa_sprintf_malloc("%lu", (unsigned long) d->UniqueID)); + pa_xfree(t); + + u->sink = pa_sink_new(m->core, &sink_data, 0); + pa_sink_new_data_done(&sink_data); + + if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->set_state = sink_set_state; + u->sink->update_requested_latency = sink_update_requested_latency; + u->sink->request_rewind = sink_request_rewind; u->sink->userdata = u; u->sink->flags = PA_SINK_LATENCY; - pa_sink_set_module(u->sink, m); - pa_sink_set_description(u->sink, t = pa_sprintf_malloc("LADSPA plugin '%s' on '%s'", label, master->description)); - pa_xfree(t); pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); /* Create sink input */ - pa_sink_input_new_data_init(&data); - data.sink = u->master; - data.driver = __FILE__; - data.name = "LADSPA Stream"; - pa_sink_input_new_data_set_sample_spec(&data, &ss); - pa_sink_input_new_data_set_channel_map(&data, &map); - data.module = m; - - if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE))) + pa_sink_input_new_data_init(&sink_input_data); + sink_input_data.driver = __FILE__; + sink_input_data.module = m; + sink_input_data.sink = u->master; + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream"); + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "routing"); + pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); + pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); + + u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new_data_done(&sink_input_data); + + if (!u->sink_input) goto fail; u->sink_input->parent.process_msg = sink_input_process_msg; - u->sink_input->peek = sink_input_peek_cb; - u->sink_input->drop = sink_input_drop_cb; + u->sink_input->pop = sink_input_pop_cb; + u->sink_input->rewind = sink_input_rewind_cb; + u->sink_input->set_max_rewind = sink_input_set_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; @@ -628,7 +665,6 @@ int pa__init(pa_module*m) { pa_modargs_free(ma); pa_xfree(use_default); - pa_xfree(default_sink_name); return 0; @@ -637,7 +673,6 @@ fail: pa_modargs_free(ma); pa_xfree(use_default); - pa_xfree(default_sink_name); pa__done(m); @@ -663,9 +698,6 @@ void pa__done(pa_module*m) { pa_sink_unref(u->sink); } - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - for (c = 0; c < u->channels; c++) if (u->handle[c]) { if (u->descriptor->deactivate) diff --git a/src/modules/module-match.c b/src/modules/module-match.c index ed5f3076..0411dcdc 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -166,6 +166,7 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v struct userdata *u = userdata; pa_sink_input *si; struct rule *r; + const char *n; pa_assert(c); pa_assert(u); @@ -176,13 +177,13 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) return; - if (!si->name) + if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME))) return; for (r = u->rules; r; r = r->next) { - if (!regexec(&r->regex, si->name, 0, NULL, 0)) { + if (!regexec(&r->regex, n, 0, NULL, 0)) { pa_cvolume cv; - pa_log_debug("changing volume of sink input '%s' to 0x%03x", si->name, r->volume); + pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume); pa_cvolume_set(&cv, si->sample_spec.channels, r->volume); pa_sink_input_set_volume(si, &cv); } diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index de35fff9..6e59c62c 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -169,6 +169,7 @@ int pa__init(pa_module*m) { pa_sample_spec ss; pa_channel_map map; pa_modargs *ma = NULL; + pa_sink_new_data data; pa_assert(m); @@ -191,7 +192,18 @@ int pa__init(pa_module*m) { u->rtpoll = pa_rtpoll_new(); pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); - if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "NULL sink")); + + u->sink = pa_sink_new(m->core, &data, 0); + pa_sink_new_data_done(&data); + + if (!u->sink) { pa_log("Failed to create sink."); goto fail; } @@ -200,10 +212,8 @@ int pa__init(pa_module*m) { u->sink->userdata = u; u->sink->flags = PA_SINK_LATENCY; - pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - pa_sink_set_description(u->sink, pa_modargs_get_value(ma, "description", "NULL sink")); u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */ if (u->block_size <= 0) diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c index a7df8a0c..149c70d5 100644 --- a/src/modules/module-oss.c +++ b/src/modules/module-oss.c @@ -1143,9 +1143,9 @@ int pa__init(pa_module*m) { pa_sample_spec ss; pa_channel_map map; pa_modargs *ma = NULL; - char hwdesc[64], *t; + char hwdesc[64]; const char *name; - int namereg_fail; + pa_bool_t namereg_fail; pa_assert(m); @@ -1258,6 +1258,7 @@ int pa__init(pa_module*m) { u->out_hwbuf_size = u->out_nfrags * u->out_fragment_size; if (mode != O_WRONLY) { + pa_source_new_data data; char *name_buf = NULL; if (use_mmap) { @@ -1270,14 +1271,28 @@ int pa__init(pa_module*m) { } if ((name = pa_modargs_get_value(ma, "source_name", NULL))) - namereg_fail = 1; + namereg_fail = TRUE; else { name = name_buf = pa_sprintf_malloc("oss_input.%s", pa_path_get_filename(dev)); - namereg_fail = 0; + namereg_fail = FALSE; } - u->source = pa_source_new(m->core, __FILE__, name, namereg_fail, &ss, &map); + pa_source_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_source_new_data_set_name(&data, name); + data.namereg_fail = namereg_fail; + pa_source_new_data_set_sample_spec(&data, &ss); + pa_source_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, dev); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "oss"); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial"); + + u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); + pa_source_new_data_done(&data); pa_xfree(name_buf); + if (!u->source) { pa_log("Failed to create source object"); goto fail; @@ -1286,18 +1301,8 @@ int pa__init(pa_module*m) { u->source->parent.process_msg = source_process_msg; u->source->userdata = u; - pa_source_set_module(u->source, m); pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); - pa_source_set_description(u->source, t = pa_sprintf_malloc( - "OSS PCM on %s%s%s%s%s", - dev, - hwdesc[0] ? " (" : "", - hwdesc[0] ? hwdesc : "", - hwdesc[0] ? ")" : "", - use_mmap ? " via DMA" : "")); - pa_xfree(t); - u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY; u->source->refresh_volume = TRUE; if (use_mmap) @@ -1305,6 +1310,7 @@ int pa__init(pa_module*m) { } if (mode != O_RDONLY) { + pa_sink_new_data data; char *name_buf = NULL; if (use_mmap) { @@ -1331,8 +1337,22 @@ int pa__init(pa_module*m) { namereg_fail = 0; } - u->sink = pa_sink_new(m->core, __FILE__, name, namereg_fail, &ss, &map); + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, name); + data.namereg_fail = namereg_fail; + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, dev); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "oss"); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial"); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); + pa_sink_new_data_done(&data); pa_xfree(name_buf); + if (!u->sink) { pa_log("Failed to create sink object"); goto fail; @@ -1341,18 +1361,8 @@ int pa__init(pa_module*m) { u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; - pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - pa_sink_set_description(u->sink, t = pa_sprintf_malloc( - "OSS PCM on %s%s%s%s%s", - dev, - hwdesc[0] ? " (" : "", - hwdesc[0] ? hwdesc : "", - hwdesc[0] ? ")" : "", - use_mmap ? " via DMA" : "")); - pa_xfree(t); - u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY; u->sink->refresh_volume = TRUE; if (use_mmap) diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index e720c8ad..73a52112 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -207,6 +207,7 @@ int pa__init(pa_module*m) { pa_modargs *ma; char *t; struct pollfd *pollfd; + pa_sink_new_data data; pa_assert(m); @@ -251,20 +252,29 @@ int pa__init(pa_module*m) { goto fail; } - if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map))) { + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Unix FIFO sink %s", u->filename)); + pa_xfree(t); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); + pa_sink_new_data_done(&data); + + if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY; - pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Unix FIFO sink '%s'", u->filename)); - pa_xfree(t); u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index 02935649..cf88c823 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -184,6 +184,7 @@ int pa__init(pa_module*m) { pa_modargs *ma; char *t; struct pollfd *pollfd; + pa_source_new_data data; pa_assert(m); @@ -228,19 +229,28 @@ int pa__init(pa_module*m) { goto fail; } - if (!(u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) { + pa_source_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Unix FIFO source %s", u->filename)); + pa_xfree(t); + pa_source_new_data_set_sample_spec(&data, &ss); + pa_source_new_data_set_channel_map(&data, &map); + + u->source = pa_source_new(m->core, &data, 0); + pa_source_new_data_done(&data); + + if (!u->source) { pa_log("Failed to create source."); goto fail; } u->source->userdata = u; - u->source->flags = 0; - pa_source_set_module(u->source, m); pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); - pa_source_set_description(u->source, t = pa_sprintf_malloc("Unix FIFO source '%s'", u->filename)); - pa_xfree(t); u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 39a9245d..1c97a824 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -59,8 +59,6 @@ struct userdata { pa_sink *sink, *master; pa_sink_input *sink_input; - - pa_memchunk memchunk; }; static const char* const valid_modargs[] = { @@ -86,7 +84,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - *((pa_usec_t*) data) = usec + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec); + *((pa_usec_t*) data) = usec/* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec)*/; return 0; } } @@ -107,13 +105,35 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { return 0; } +/* Called from I/O thread context */ +static void sink_request_rewind(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + pa_sink_input_request_rewrite(u->sink_input, s->thread_info.rewind_nbytes); +} + +/* Called from I/O thread context */ +static void sink_update_requested_latency(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + pa_assert_se(u = s->userdata); + + /* Just hand this one over to the master sink */ + u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency(s); + pa_sink_invalidate_requested_latency(u->master); +} + /* Called from I/O thread context */ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK_INPUT(o)->userdata; switch (code) { case PA_SINK_INPUT_MESSAGE_GET_LATENCY: - *((pa_usec_t*) data) = pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec); + *((pa_usec_t*) data) = 0; /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);*/ /* Fall through, the default handler will add in the extra * latency added by the resampler */ @@ -123,45 +143,41 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t return pa_sink_input_process_msg(o, code, data, offset, chunk); } +static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes); + /* Called from I/O thread context */ -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; pa_sink_input_assert_ref(i); + pa_assert(chunk); pa_assert_se(u = i->userdata); - if (!u->memchunk.memblock) - pa_sink_render(u->sink, length, &u->memchunk); + pa_sink_render(u->sink, nbytes, chunk); - pa_assert(u->memchunk.memblock); - *chunk = u->memchunk; - pa_memblock_ref(chunk->memblock); return 0; } /* Called from I/O thread context */ -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { +static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_assert(length > 0); + pa_assert(nbytes > 0); - if (u->memchunk.memblock) { + u->sink->thread_info.rewind_nbytes = nbytes; + pa_sink_process_rewind(u->sink); +} - if (length < u->memchunk.length) { - u->memchunk.index += length; - u->memchunk.length -= length; - return; - } +/* Called from I/O thread context */ +static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct userdata *u; - pa_memblock_unref(u->memchunk.memblock); - length -= u->memchunk.length; - pa_memchunk_reset(&u->memchunk); - } + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); - if (length > 0) - pa_sink_skip(u->sink, length); + pa_sink_set_max_rewind(u->sink, nbytes); } /* Called from I/O thread context */ @@ -211,9 +227,10 @@ int pa__init(pa_module*m) { pa_channel_map sink_map, stream_map; pa_modargs *ma; char *t; + const char *k; pa_sink *master; - pa_sink_input_new_data data; - char *default_sink_name = NULL; + pa_sink_input_new_data sink_input_data; + pa_sink_new_data sink_data; pa_assert(m); @@ -245,47 +262,68 @@ int pa__init(pa_module*m) { goto fail; } + if (pa_channel_map_equal(&stream_map, &master->channel_map)) + pa_log_warn("No remapping configured, proceeding nonetheless!"); + u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; m->userdata = u; u->master = master; - pa_memchunk_reset(&u->memchunk); - - default_sink_name = pa_sprintf_malloc("%s.remapped", master->name); + u->sink = NULL; + u->sink_input = NULL; /* Create sink */ - if (!(u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", default_sink_name), 0, &ss, &sink_map))) { + pa_sink_new_data_init(&sink_data); + sink_data.driver = __FILE__; + sink_data.module = m; + if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) + sink_data.name = pa_sprintf_malloc("%s.remapped", master->name); + pa_sink_new_data_set_sample_spec(&sink_data, &ss); + pa_sink_new_data_set_channel_map(&sink_data, &sink_map); + k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Remapped %s", k ? k : master->name)); + pa_xfree(t); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); + + u->sink = pa_sink_new(m->core, &sink_data, 0); + pa_sink_new_data_done(&sink_data); + + if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->set_state = sink_set_state; + u->sink->update_requested_latency = sink_update_requested_latency; + u->sink->request_rewind = sink_request_rewind; u->sink->userdata = u; u->sink->flags = PA_SINK_LATENCY; - pa_sink_set_module(u->sink, m); - pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Remapped %s", master->description)); - pa_xfree(t); pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); /* Create sink input */ - pa_sink_input_new_data_init(&data); - data.sink = u->master; - data.driver = __FILE__; - data.name = "Remapped Stream"; - pa_sink_input_new_data_set_sample_spec(&data, &ss); - pa_sink_input_new_data_set_channel_map(&data, &stream_map); - data.module = m; - - if (!(u->sink_input = pa_sink_input_new(m->core, &data, PA_SINK_INPUT_DONT_MOVE))) + pa_sink_input_new_data_init(&sink_input_data); + sink_input_data.driver = __FILE__; + sink_input_data.module = m; + sink_input_data.sink = u->master; + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream"); + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "routing"); + pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); + pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map); + + u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE); + pa_sink_input_new_data_done(&sink_input_data); + + if (!u->sink_input) goto fail; u->sink_input->parent.process_msg = sink_input_process_msg; - u->sink_input->peek = sink_input_peek_cb; - u->sink_input->drop = sink_input_drop_cb; + u->sink_input->pop = sink_input_pop_cb; + u->sink_input->rewind = sink_input_rewind_cb; + u->sink_input->set_max_rewind = sink_input_set_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; @@ -295,7 +333,6 @@ int pa__init(pa_module*m) { pa_sink_input_put(u->sink_input); pa_modargs_free(ma); - pa_xfree(default_sink_name); return 0; @@ -305,8 +342,6 @@ fail: pa__done(m); - pa_xfree(default_sink_name); - return -1; } @@ -328,8 +363,5 @@ void pa__done(pa_module*m) { pa_sink_unref(u->sink); } - if (u->memchunk.memblock) - pa_memblock_unref(u->memchunk.memblock); - pa_xfree(u); } diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index 12957c9d..dda54735 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -76,11 +76,11 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user while ((i = pa_idxset_first(sink->inputs, NULL))) { if (pa_sink_input_move_to(i, target, 1) < 0) { - pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, i->name, target->name); + pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name); return PA_HOOK_OK; } - pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, i->name, target->name); + pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name); } @@ -116,11 +116,11 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void while ((o = pa_idxset_first(source->outputs, NULL))) { if (pa_source_output_move_to(o, target) < 0) { - pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, o->name, target->name); + pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name); return PA_HOOK_OK; } - pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, o->name, target->name); + pa_log_info("Sucessfully moved source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name); } diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index 41d9a51c..f6718fd8 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -59,44 +59,25 @@ static const char* const valid_modargs[] = { NULL, }; -static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; - pa_assert(i); - u = i->userdata; - pa_assert(u); + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); pa_assert(chunk); chunk->memblock = pa_memblock_ref(u->memblock); - chunk->index = u->peek_index; - chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index; + chunk->length = pa_memblock_get_length(chunk->memblock); + chunk->index = 0; return 0; } -static void sink_input_drop_cb(pa_sink_input *i, size_t length) { - struct userdata *u; - size_t l; - - pa_assert(i); - u = i->userdata; - pa_assert(u); - pa_assert(length > 0); - - u->peek_index += length; - - l = pa_memblock_get_length(u->memblock); - - while (u->peek_index >= l) - u->peek_index -= l; -} - static void sink_input_kill_cb(pa_sink_input *i) { struct userdata *u; - pa_assert(i); - u = i->userdata; - pa_assert(u); + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); pa_sink_input_unlink(u->sink_input); pa_sink_input_unref(u->sink_input); @@ -156,20 +137,23 @@ int pa__init(pa_module*m) { calc_sine(p, pa_memblock_get_length(u->memblock), frequency); pa_memblock_release(u->memblock); - pa_snprintf(t, sizeof(t), "Sine Generator at %u Hz", frequency); + pa_snprintf(t, sizeof(t), "%u Hz Sine", frequency); pa_sink_input_new_data_init(&data); data.sink = sink; data.driver = __FILE__; - data.name = t; + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, t); + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); pa_sink_input_new_data_set_sample_spec(&data, &ss); data.module = m; - if (!(u->sink_input = pa_sink_input_new(m->core, &data, 0))) + u->sink_input = pa_sink_input_new(m->core, &data, 0); + pa_sink_input_new_data_done(&data); + + if (!u->sink_input) goto fail; - u->sink_input->peek = sink_input_peek_cb; - u->sink_input->drop = sink_input_drop_cb; + u->sink_input->pop = sink_input_pop_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->userdata = u; diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index 4c260d76..ef8239d0 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -367,8 +367,8 @@ int pa__init(pa_module*m) { for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx)) device_new_hook_cb(m->core, PA_OBJECT(source), u); - u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) device_new_hook_cb, u); - u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], (pa_hook_cb_t) device_new_hook_cb, u); + u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], (pa_hook_cb_t) device_new_hook_cb, u); + u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], (pa_hook_cb_t) device_new_hook_cb, u); u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], (pa_hook_cb_t) device_unlink_hook_cb, u); u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK_POST], (pa_hook_cb_t) device_unlink_hook_cb, u); u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], (pa_hook_cb_t) device_state_changed_hook_cb, u); diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index a53e3932..2da2d134 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -1294,6 +1294,11 @@ int pa__init(pa_module*m) { pa_sample_spec ss; pa_channel_map map; char *t, *dn = NULL; +#ifdef TUNNEL_SINK + pa_sink_new_data data; +#else + pa_source_new_data data; +#endif pa_assert(m); @@ -1354,7 +1359,18 @@ int pa__init(pa_module*m) { if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) dn = pa_sprintf_malloc("tunnel.%s", u->server_name); - if (!(u->sink = pa_sink_new(m->core, __FILE__, dn, 1, &ss, &map))) { + pa_sink_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + data.namereg_fail = TRUE; + pa_sink_new_data_set_name(&data, dn); + pa_sink_new_data_set_sample_spec(&data, &ss); + pa_sink_new_data_set_channel_map(&data, &map); + + u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL); + pa_sink_new_data_done(&data); + + if (!u->sink) { pa_log("Failed to create sink."); goto fail; } @@ -1367,9 +1383,7 @@ int pa__init(pa_module*m) { u->sink->get_mute = sink_get_mute; u->sink->set_volume = sink_set_volume; u->sink->set_mute = sink_set_mute; - u->sink->flags = PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL; - pa_sink_set_module(u->sink, m); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_description(u->sink, t = pa_sprintf_malloc("%s%s%s", u->sink_name ? u->sink_name : "", u->sink_name ? " on " : "", u->server_name)); @@ -1380,7 +1394,18 @@ int pa__init(pa_module*m) { if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL)))) dn = pa_sprintf_malloc("tunnel.%s", u->server_name); - if (!(u->source = pa_source_new(m->core, __FILE__, dn, 1, &ss, &map))) { + pa_source_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + data.namereg_fail = TRUE; + pa_source_new_data_set_name(&data, dn); + pa_source_new_data_set_sample_spec(&data, &ss); + pa_source_new_data_set_channel_map(&data, &map); + + u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY); + pa_source_new_data_done(&data); + + if (!u->source) { pa_log("Failed to create source."); goto fail; } @@ -1389,9 +1414,7 @@ int pa__init(pa_module*m) { u->source->userdata = u; u->source->set_state = source_set_state; u->source->get_latency = source_get_latency; - u->source->flags = PA_SOURCE_NETWORK|PA_SOURCE_LATENCY; - pa_source_set_module(u->source, m); pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); pa_source_set_description(u->source, t = pa_sprintf_malloc("%s%s%s", u->source_name ? u->source_name : "", u->source_name ? " on " : "", u->server_name)); diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index 192a2a78..0dc8dcfd 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -115,7 +115,7 @@ static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) { k = strtol(p, &p, 0); - if (k < PA_VOLUME_MUTED) + if (k < (long) PA_VOLUME_MUTED) return NULL; v->values[i] = (pa_volume_t) k; @@ -280,10 +280,10 @@ finish: static char* client_name(pa_client *c) { char *t, *e; - if (!c->name || !c->driver) + if (!pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME) || !c->driver) return NULL; - t = pa_sprintf_malloc("%s$%s", c->driver, c->name); + t = pa_sprintf_malloc("%s$%s", c->driver, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)); t[strcspn(t, "\n\r#")] = 0; if (!*t) { diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c index 87c6849d..761b82a9 100644 --- a/src/modules/module-x11-bell.c +++ b/src/modules/module-x11-bell.c @@ -81,7 +81,7 @@ static int x11_event_callback(pa_x11_wrapper *w, XEvent *e, void *userdata) { bne = (XkbBellNotifyEvent*) e; - if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, (bne->percent*PA_VOLUME_NORM)/100, 1) < 0) { + if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, (bne->percent*PA_VOLUME_NORM)/100, NULL, NULL) < 0) { pa_log_info("Ringing bell failed, reverting to X11 device bell."); XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent); } diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c index 46969a24..6ed8e3d9 100644 --- a/src/modules/module-zeroconf-publish.c +++ b/src/modules/module-zeroconf-publish.c @@ -115,7 +115,7 @@ static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_chann *ret_ss = sink->sample_spec; *ret_map = sink->channel_map; *ret_name = sink->name; - *ret_description = sink->description; + *ret_description = pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL; } else if (pa_source_isinstance(s->device)) { @@ -124,7 +124,7 @@ static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_chann *ret_ss = source->sample_spec; *ret_map = source->channel_map; *ret_name = source->name; - *ret_description = source->description; + *ret_description = pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)); *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL); } else @@ -304,10 +304,10 @@ static struct service *get_service(struct userdata *u, pa_object *device) { s->device = device; if (pa_sink_isinstance(device)) { - if (!(n = PA_SINK(device)->description)) + if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION))) n = PA_SINK(device)->name; } else { - if (!(n = PA_SOURCE(device)->description)) + if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION))) n = PA_SOURCE(device)->name; } @@ -578,11 +578,11 @@ int pa__init(pa_module*m) { u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); - u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW_POST], (pa_hook_cb_t) device_new_or_changed_cb, u); - u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_DESCRIPTION_CHANGED], (pa_hook_cb_t) device_new_or_changed_cb, u); + u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], (pa_hook_cb_t) device_new_or_changed_cb, u); + u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], (pa_hook_cb_t) device_new_or_changed_cb, u); u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], (pa_hook_cb_t) device_unlink_cb, u); - u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW_POST], (pa_hook_cb_t) device_new_or_changed_cb, u); - u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_DESCRIPTION_CHANGED], (pa_hook_cb_t) device_new_or_changed_cb, u); + u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], (pa_hook_cb_t) device_new_or_changed_cb, u); + u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], (pa_hook_cb_t) device_new_or_changed_cb, u); u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], (pa_hook_cb_t) device_unlink_cb, u); u->main_entry_group = NULL; -- cgit From b5c5064a594a4930d9b82752116cce5a11bdd81b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Mar 2008 15:24:36 +0000 Subject: commit glitch-free work git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2125 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 3 ++- src/modules/alsa-util.h | 15 ++++++++++++--- src/modules/rtp/module-rtp-recv.c | 23 ++++++++++------------- src/modules/rtp/module-rtp-send.c | 8 ++++++-- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index a3bb4d0e..df2638c4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -695,7 +695,8 @@ libpulsecore_la_SOURCES = \ pulse/utf8.c pulse/utf8.h \ pulse/util.c pulse/util.h \ pulse/volume.c pulse/volume.h \ - pulse/xmalloc.c pulse/xmalloc.h + pulse/xmalloc.c pulse/xmalloc.h \ + pulse/proplist.c pulse/proplist.h # Pure core stuff (some are shared in libpulse though). libpulsecore_la_SOURCES += \ diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 53d9a2fb..62c1d434 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -43,10 +43,12 @@ int pa_alsa_set_hw_params( pa_sample_spec *ss, uint32_t *periods, snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, + pa_bool_t *use_tsched, pa_bool_t require_exact_channel_number); -int pa_alsa_set_sw_params(snd_pcm_t *pcm); +int pa_alsa_set_sw_params(snd_pcm_t *pcm, size_t avail_min); int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev); snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback); @@ -59,7 +61,9 @@ snd_pcm_t *pa_alsa_open_by_device_id( int mode, uint32_t *nfrags, snd_pcm_uframes_t *period_size, - pa_bool_t *use_mmap); + snd_pcm_uframes_t tsched_size, + pa_bool_t *use_mmap, + pa_bool_t *use_tsched); snd_pcm_t *pa_alsa_open_by_device_string( const char *device, @@ -69,8 +73,13 @@ snd_pcm_t *pa_alsa_open_by_device_string( int mode, uint32_t *nfrags, snd_pcm_uframes_t *period_size, - pa_bool_t *use_mmap); + snd_pcm_uframes_t tsched_size, + pa_bool_t *use_mmap, + pa_bool_t *use_tsched); int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback); +void pa_alsa_0dB_playback(snd_mixer_elem_t *elem); +void pa_alsa_0dB_capture(snd_mixer_elem_t *elem); + #endif diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index d8e7a781..be54c385 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -133,21 +133,17 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t } /* Called from I/O thread context */ -static int sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop(pa_sink_input *i, size_t length, pa_memchunk *chunk) { struct session *s; pa_sink_input_assert_ref(i); pa_assert_se(s = i->userdata); - return pa_memblockq_peek(s->memblockq, chunk); -} + if (pa_memblockq_peek(s->memblockq, chunk) < 0) + return -1; -/* Called from I/O thread context */ -static void sink_input_drop(pa_sink_input *i, size_t length) { - struct session *s; - pa_sink_input_assert_ref(i); - pa_assert_se(s = i->userdata); + pa_memblockq_drop(s->memblockq, chunk->length); - pa_memblockq_drop(s->memblockq, length); + return 0; } /* Called from main context */ @@ -354,12 +350,13 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in pa_sink_input_new_data_init(&data); data.sink = sink; data.driver = __FILE__; - data.name = c; + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, c); + pa_xfree(c); data.module = u->module; pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec); s->sink_input = pa_sink_input_new(u->module->core, &data, 0); - pa_xfree(c); + pa_sink_input_new_data_done(&data); if (!s->sink_input) { pa_log("Failed to create sink input."); @@ -369,8 +366,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in s->sink_input->userdata = s; s->sink_input->parent.process_msg = sink_input_process_msg; - s->sink_input->peek = sink_input_peek; - s->sink_input->drop = sink_input_drop; + s->sink_input->pop = sink_input_pop; s->sink_input->kill = sink_input_kill; s->sink_input->attach = sink_input_attach; s->sink_input->detach = sink_input_detach; @@ -387,6 +383,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in pa_frame_size(&s->sink_input->sample_spec), pa_bytes_per_second(&s->sink_input->sample_spec)/10+1, 0, + 0, silence); pa_memblock_unref(silence); diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index 95ff15de..a4417538 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -288,14 +288,17 @@ int pa__init(pa_module*m) { pa_make_fd_cloexec(sap_fd); pa_source_output_new_data_init(&data); - data.name = "RTP Monitor Stream"; + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream"); data.driver = __FILE__; data.module = m; data.source = s; pa_source_output_new_data_set_sample_spec(&data, &ss); pa_source_output_new_data_set_channel_map(&data, &cm); - if (!(o = pa_source_output_new(m->core, &data, 0))) { + o = pa_source_output_new(m->core, &data, 0); + pa_source_output_new_data_done(&data); + + if (!o) { pa_log("failed to create source output."); goto fail; } @@ -318,6 +321,7 @@ int pa__init(pa_module*m) { pa_frame_size(&ss), 1, 0, + 0, NULL); u->mtu = mtu; -- cgit From 106ddb9211a98dec764ca45ca02b46c31354e631 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 15 Mar 2008 15:26:03 +0000 Subject: remaining bits and pieces git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2126 fefdeb5f-60dc-0310-8127-8f9354f1896f --- PROTOCOL | 39 +++++++++++++++++++++++++++++++++++++++ configure.ac | 1 + 2 files changed, 40 insertions(+) diff --git a/PROTOCOL b/PROTOCOL index 497fa47b..40863fc2 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -78,3 +78,42 @@ New opcodes for notifications: PA_COMMAND_CAPTURE_STREAM_SUSPENDED PA_COMMAND_PLAYBACK_STREAM_MOVED PA_COMMAND_CAPTURE_STREAM_MOVED + +### v13, implemented by >= 0.9.10 + +New fields for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM request at the end: + + peak_detect (bool) + +Replace field "name" for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM at the end: + + proplist + +Replace field "name" for PA_COMMAND_SET_CLIENT_NAME request at the end: + + proplist + +On response of PA_COMMAND_SET_CLIENT_NAME: + + client_index + +New proplist field for sink, source, sink input, source output introspection opcodes and at the end: + + proplist + +New opcodes for proplist modifications + + PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST + PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST + PA_COMMAND_UPDATE_CLIENT_PROPLIST + PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST + PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST + PA_COMMAND_REMOVE_CLIENT_PROPLIST + +New field for PA_COMMAND_PLAY_SAMPLE: + + proplist + +New field for PA_COMMAND_PLAY_SAMPLE response: + + idx diff --git a/configure.ac b/configure.ac index ba8f2c96..748e8f04 100644 --- a/configure.ac +++ b/configure.ac @@ -257,6 +257,7 @@ AC_CHECK_TYPES(ssize_t, , [AC_DEFINE([ssize_t], [signed long], AC_TYPE_OFF_T AC_TYPE_SIGNAL AC_TYPE_UID_T +AC_CHECK_DECLS(environ) AC_CHECK_DEFINE([SIGXCPU], [signal.h], [ HAVE_SIGXCPU=1 -- cgit From ecf643966111387953cbfd0bce7f39b6c3d8116a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 31 Mar 2008 23:08:01 +0000 Subject: catch up with trunk HEAD (i.e. 2118:2213) git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2214 fefdeb5f-60dc-0310-8127-8f9354f1896f --- configure.ac | 190 ++++++++++++++++++++++-------- src/Makefile.am | 27 +++-- src/daemon/PulseAudio.policy | 49 -------- src/daemon/caps.c | 28 ++--- src/daemon/caps.h | 2 +- src/daemon/cpulimit.c | 2 +- src/daemon/daemon.conf.in | 2 +- src/daemon/default.pa.in | 20 ++-- src/daemon/main.c | 18 +-- src/daemon/org.pulseaudio.policy | 52 +++++++++ src/daemon/polkit.c | 78 ++----------- src/modules/bt-proximity-helper.c | 38 +++--- src/modules/module-ladspa-sink.c | 3 +- src/modules/module-protocol-stub.c | 5 +- src/modules/module-tunnel.c | 4 +- src/pulse/util.c | 7 +- src/pulsecore/atomic.h | 231 ++++++++++++++++++++++++++++++++++++- src/pulsecore/cli-command.c | 32 ++++- src/pulsecore/hashmap.c | 8 +- src/pulsecore/macro.h | 58 ++++++---- src/pulsecore/protocol-esound.c | 2 +- src/pulsecore/protocol-native.c | 1 + src/tests/smoother-test.c | 2 +- src/utils/pactl.c | 2 +- src/utils/padsp.c | 4 + 25 files changed, 597 insertions(+), 268 deletions(-) delete mode 100644 src/daemon/PulseAudio.policy create mode 100644 src/daemon/org.pulseaudio.policy diff --git a/configure.ac b/configure.ac index 748e8f04..112f6329 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ # This file is part of PulseAudio. # -# Copyright 2004-2006 Lennart Poettering +# Copyright 2004-2008 Lennart Poettering # Copyright 2006-2007 Pierre Ossman for Cendio AB # # PulseAudio is free software; you can redistribute it and/or modify it @@ -22,11 +22,11 @@ # along with PulseAudio; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -AC_PREREQ(2.57) +AC_PREREQ(2.60) m4_define(PA_MAJOR, [0]) m4_define(PA_MINOR, [9]) -m4_define(PA_MICRO, [8]) +m4_define(PA_MICRO, [10]) AC_INIT([pulseaudio], PA_MAJOR.PA_MINOR.PA_MICRO,[mzchyfrnhqvb (at) 0pointer (dot) net]) AC_CONFIG_SRCDIR([src/daemon/main.c]) @@ -41,7 +41,7 @@ AC_SUBST(PA_PROTOCOL_VERSION, 12) # The stable ABI for client applications, for the version info x:y:z # always will hold y=z -AC_SUBST(LIBPULSE_VERSION_INFO, [4:0:4]) +AC_SUBST(LIBPULSE_VERSION_INFO, [4:1:4]) # A simplified, synchronous, ABI-stable interface for client # applications, for the version info x:y:z always will hold y=z @@ -58,7 +58,7 @@ AC_SUBST(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO, [0:3:0]) # An internally used, ABI-unstable library that contains the # PulseAudio core, SONAMEs are bumped on every release, version info # suffix will always be 0:0 -AC_SUBST(LIBPULSECORE_VERSION_INFO, [5:0:0]) +AC_SUBST(LIBPULSECORE_VERSION_INFO, [5:1:0]) AC_CANONICAL_HOST @@ -125,6 +125,43 @@ if test "x$GCC" = "xyes" ; then done fi +# Native atomic operation support +AC_ARG_ENABLE([atomic-arm-linux-helpers], + AC_HELP_STRING([--disable-atomic-arm-linux-helpers], [use inline asm or libatomic_ops instead]), + [ + case "${enableval}" in + yes) atomic_arm_linux_helpers=yes ;; + no) atomic_arm_linux_helpers=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-atomic-arm-linux-helpers) ;; + esac + ], + [atomic_arm_linux_helpers=auto]) + +AC_ARG_ENABLE([atomic-arm-memory-barrier], + AC_HELP_STRING([--enable-atomic-arm-memory-barrier], [only really needed in SMP arm systems]), + [ + case "${enableval}" in + yes) AC_DEFINE_UNQUOTED(ATOMIC_ARM_MEMORY_BARRIER_ENABLED, 1, [Enable memory barriers]) ;; + no) ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-atomic-arm-linux-helpers) ;; + esac + ],) + +AC_MSG_CHECKING([target operating system]) +case $host in + *-*-linux*) + AC_MSG_RESULT([linux]) + pulse_target_os=linux + ;; + *) + AC_MSG_RESULT([unknown]) + pulse_target_os=unknown + ;; +esac + +# If everything else fails use libatomic_ops +need_libatomic_ops=yes + AC_MSG_CHECKING([whether $CC knows __sync_bool_compare_and_swap()]) AC_LANG_CONFTEST([int main() { int a = 4; __sync_bool_compare_and_swap(&a, 4, 5); }]) $CC conftest.c $CFLAGS -o conftest > /dev/null 2> /dev/null @@ -133,8 +170,53 @@ rm -f conftest.o conftest if test $ret -eq 0 ; then AC_DEFINE([HAVE_ATOMIC_BUILTINS], 1, [Have __sync_bool_compare_and_swap() and friends.]) AC_MSG_RESULT([yes]) + need_libatomic_ops=no else AC_MSG_RESULT([no]) + # HW specific atomic ops stuff + AC_MSG_CHECKING([architecture for native atomic operations]) + case $host_cpu in + arm*) + AC_MSG_RESULT([arm]) + AC_MSG_CHECKING([whether we can use Linux kernel helpers]) + # The Linux kernel helper functions have been there since 2.6.16. However + # compile time checking for kernel version in cross compile environment + # (which is usually the case for arm cpu) is tricky (or impossible). + if test "x$pulse_target_os" = "xlinux" && test "x$atomic_arm_linux_helpers" != "xno"; then + AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(ATOMIC_ARM_LINUX_HELPERS, 1, [special arm linux implementation]) + need_libatomic_ops=no + else + AC_MSG_RESULT([no]) + AC_MSG_CHECKING([compiler support for arm inline asm atomic operations]) + AC_LANG_CONFTEST([[int main() + { + volatile int a=0; + int o=0, n=1, r; + asm volatile ("ldrex %0, [%1]\n" + "subs %0, %0, %2\n" + "strexeq %0, %3, [%1]\n" + : "=&r" (r) + : "r" (&a), "Ir" (o), "r" (n) + : "cc"); + return (a==1 ? 0 : -1); + }]]) + $CC conftest.c $CFLAGS -o conftest > /dev/null 2>&1 + ret=$? + rm -f conftest.o conftest + if test $ret -eq 0 ; then + AC_DEFINE([ATOMIC_ARM_INLINE_ASM], 1, [Have ARMv6 instructions.]) + AC_MSG_RESULT([yes]) + need_libatomic_ops=no + else + AC_MSG_RESULT([no]) + fi + fi + ;; + *) + AC_MSG_RESULT([unknown]) + ;; + esac fi AC_MSG_CHECKING([whether $CC knows __thread]) @@ -413,13 +495,19 @@ AC_SUBST(LIBSNDFILE_LIBS) #### atomic-ops ### -AC_CHECK_HEADERS([atomic_ops.h], [], [ -AC_MSG_ERROR([*** libatomic-ops headers not found]) -]) - -# Win32 does not need the lib and breaks horribly if we try to include it -if test "x$os_is_win32" != "x1" ; then - LIBS="$LIBS -latomic_ops" +AC_MSG_CHECKING([whether we need libatomic_ops]) +if test "x$need_libatomic_ops" = "xyes"; then + AC_MSG_RESULT([yes]) + AC_CHECK_HEADERS([atomic_ops.h], [], [ + AC_MSG_ERROR([*** libatomic-ops headers not found]) + ]) + + # Win32 does not need the lib and breaks horribly if we try to include it + if test "x$os_is_win32" != "x1" ; then + LIBS="$LIBS -latomic_ops" + fi +else + AC_MSG_RESULT([no]) fi #### Libsamplerate support (optional) #### @@ -889,13 +977,9 @@ AC_ARG_ENABLE([polkit], if test "x${polkit}" != xno ; then - PKG_CHECK_MODULES(POLKIT, [ polkit-dbus ], + PKG_CHECK_MODULES(POLKIT, [ polkit-dbus >= 0.7 ], [ HAVE_POLKIT=1 - saved_LIBS="$LIBS" - LIBS="$LIBS $POLKIT_LIBS" - AC_CHECK_FUNCS(polkit_context_is_caller_authorized) - LIBS="$saved_LIBS" AC_DEFINE([HAVE_POLKIT], 1, [Have PolicyKit]) policydir=`pkg-config polkit-dbus --variable prefix`/share/PolicyKit/policy/ AC_SUBST(policydir) @@ -990,10 +1074,20 @@ fi AC_SUBST(PA_ACCESS_GROUP) AC_DEFINE_UNQUOTED(PA_ACCESS_GROUP,"$PA_ACCESS_GROUP", [Access group]) -AC_ARG_WITH(peruser_esound, AS_HELP_STRING([--with-peruser-esound-socket], [Use per-user esound socket directory, like /tmp/.esd-UID/socket.])) +AC_ARG_ENABLE( + per_user_esound_socket, + AS_HELP_STRING([--disable-per-user-esound-socket], [Use global esound socket directory /tmp/.esd/socket.]), + [ + case "${enableval}" in + yes) per_user_esound_socket=1 ;; + no) per_user_esound_socket=0 ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-per-user-esound-socket) ;; + esac + ], + [per_user_esound_socket=1]) -if test "x$with_peruser_esound" = "xyes"; then - AC_DEFINE([USE_PERUSER_ESOUND_SOCKET], [1], [Define this if you want per-user esound socket directories]) +if test "x$per_user_esound_socket" = "x1"; then + AC_DEFINE([USE_PER_USER_ESOUND_SOCKET], [1], [Define this if you want per-user esound socket directories]) fi #### PulseAudio system runtime dir #### @@ -1127,32 +1221,38 @@ if test "x${HAVE_POLKIT}" = "x1" ; then ENABLE_POLKIT=yes fi +ENABLE_PER_USER_ESOUND_SOCKET=no +if test "x$per_user_esound_socket" = "x1" ; then + ENABLE_PER_USER_ESOUND_SOCKET=yes +fi + echo " ---{ $PACKAGE_NAME $VERSION }--- - prefix: ${prefix} - sysconfdir: ${sysconfdir} - localstatedir: ${localstatedir} - System Runtime Path: ${PA_SYSTEM_RUNTIME_PATH} - Compiler: ${CC} - CFLAGS: ${CFLAGS} - Have X11: ${ENABLE_X11} - Enable OSS: ${ENABLE_OSS} - Enable Alsa: ${ENABLE_ALSA} - Enable Solaris: ${ENABLE_SOLARIS} - Enable GLib 2.0: ${ENABLE_GLIB20} - Enable GConf: ${ENABLE_GCONF} - Enable Avahi: ${ENABLE_AVAHI} - Enable Jack: ${ENABLE_JACK} - Enable Async DNS: ${ENABLE_LIBASYNCNS} - Enable LIRC: ${ENABLE_LIRC} - Enable HAL: ${ENABLE_HAL} - Enable BlueZ: ${ENABLE_BLUEZ} - Enable TCP Wrappers: ${ENABLE_TCPWRAP} - Enable libsamplerate: ${ENABLE_LIBSAMPLERATE} - Enable PolicyKit: ${ENABLE_POLKIT} - System User: ${PA_SYSTEM_USER} - System Group: ${PA_SYSTEM_GROUP} - Realtime Group: ${PA_REALTIME_GROUP} - Access Group: ${PA_ACCESS_GROUP} + prefix: ${prefix} + sysconfdir: ${sysconfdir} + localstatedir: ${localstatedir} + System Runtime Path: ${PA_SYSTEM_RUNTIME_PATH} + Compiler: ${CC} + CFLAGS: ${CFLAGS} + Have X11: ${ENABLE_X11} + Enable OSS: ${ENABLE_OSS} + Enable Alsa: ${ENABLE_ALSA} + Enable Solaris: ${ENABLE_SOLARIS} + Enable GLib 2.0: ${ENABLE_GLIB20} + Enable GConf: ${ENABLE_GCONF} + Enable Avahi: ${ENABLE_AVAHI} + Enable Jack: ${ENABLE_JACK} + Enable Async DNS: ${ENABLE_LIBASYNCNS} + Enable LIRC: ${ENABLE_LIRC} + Enable HAL: ${ENABLE_HAL} + Enable BlueZ: ${ENABLE_BLUEZ} + Enable TCP Wrappers: ${ENABLE_TCPWRAP} + Enable libsamplerate: ${ENABLE_LIBSAMPLERATE} + Enable PolicyKit: ${ENABLE_POLKIT} + System User: ${PA_SYSTEM_USER} + System Group: ${PA_SYSTEM_GROUP} + Realtime Group: ${PA_REALTIME_GROUP} + Access Group: ${PA_ACCESS_GROUP} + Enable per-user EsounD socket: ${ENABLE_PER_USER_ESOUND_SOCKET} " diff --git a/src/Makefile.am b/src/Makefile.am index df2638c4..e537e97b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,7 +64,7 @@ AM_LIBADD = $(PTHREAD_LIBS) AM_LDADD = $(PTHREAD_LIBS) # Only required on some platforms but defined for all to avoid errors -AM_LDFLAGS = -Wl,-no-undefined -ffunction-sections -fdata-sections -Wl,--gc-sections +AM_LDFLAGS = -Wl,-no-undefined -Wl,--gc-sections if STATIC_BINS BINLDFLAGS = -static @@ -103,7 +103,7 @@ EXTRA_DIST = \ modules/module-defs.h.m4 \ daemon/pulseaudio-module-xsmp.desktop \ map-file \ - daemon/PulseAudio.policy + daemon/org.pulseaudio.policy pulseconf_DATA = \ default.pa \ @@ -154,7 +154,7 @@ endif if HAVE_POLKIT -policy_DATA = daemon/PulseAudio.policy +policy_DATA = daemon/org.pulseaudio.policy pulseaudio_SOURCES += daemon/polkit.c daemon/polkit.h pulseaudio_CFLAGS += $(POLKIT_CFLAGS) @@ -202,7 +202,7 @@ pactl_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS) pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -pasuspender_SOURCES = utils/pasuspender.c +pasuspender_SOURCES = utils/pasuspender.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS) pasuspender_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS) pasuspender_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) pasuspender_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) @@ -275,7 +275,7 @@ mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) thread_mainloop_test_SOURCES = tests/thread-mainloop-test.c thread_mainloop_test_CFLAGS = $(AM_CFLAGS) -thread_mainloop_test_LDADD = $(AM_LDADD) libpulse.la +thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore.la libpulse.la thread_mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) utf8_test_SOURCES = tests/utf8-test.c @@ -577,7 +577,14 @@ libpulse_la_CFLAGS += $(LIBASYNCNS_CFLAGS) libpulse_la_LIBADD += $(LIBASYNCNS_LIBS) endif -libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h +libpulse_simple_la_SOURCES = \ + pulse/simple.c pulse/simple.h \ + pulsecore/log.c pulsecore/log.h \ + pulsecore/core-util.c pulsecore/core-util.h \ + pulsecore/core-error.c pulsecore/core-error.h \ + pulsecore/once.c pulsecore/once.h \ + $(PA_THREAD_OBJS) + libpulse_simple_la_CFLAGS = $(AM_CFLAGS) libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file @@ -587,7 +594,13 @@ libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS) libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la $(AVAHI_LIBS) libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file -libpulse_mainloop_glib_la_SOURCES = pulse/glib-mainloop.h pulse/glib-mainloop.c +libpulse_mainloop_glib_la_SOURCES = \ + pulse/glib-mainloop.h pulse/glib-mainloop.c \ + pulsecore/log.c pulsecore/log.h \ + pulsecore/core-util.c pulsecore/core-util.h \ + pulsecore/core-error.c pulsecore/core-error.h \ + pulsecore/once.c pulsecore/once.h \ + $(PA_THREAD_OBJS) libpulse_mainloop_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS) libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la $(GLIB20_LIBS) libpulse_mainloop_glib_la_LDFLAGS = -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file diff --git a/src/daemon/PulseAudio.policy b/src/daemon/PulseAudio.policy deleted file mode 100644 index cf9499ee..00000000 --- a/src/daemon/PulseAudio.policy +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - Real-time scheduling for the PulseAudio daemon - System policy prevents PulseAudio from acquiring real-time scheduling. - - no - no - no - - - - - High-priority scheduling (negative Unix nice level) for the PulseAudio daemon - System policy prevents PulseAudio from acquiring high-priority scheduling. - - no - no - no - - - - diff --git a/src/daemon/caps.c b/src/daemon/caps.c index 44ee355e..d78e9689 100644 --- a/src/daemon/caps.c +++ b/src/daemon/caps.c @@ -90,8 +90,8 @@ int pa_limit_caps(void) { cap_t caps; cap_value_t nice_cap = CAP_SYS_NICE; - caps = cap_init(); - pa_assert(caps); + pa_assert_se(caps = cap_init()); + cap_clear(caps); cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET); cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET); @@ -113,28 +113,15 @@ fail: } /* Drop all capabilities, effectively becoming a normal user */ -int pa_drop_caps(void) { +void pa_drop_caps(void) { cap_t caps; - int r = -1; - caps = cap_init(); - pa_assert(caps); + pa_assert_se(prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == 0); + pa_assert_se(caps = cap_init()); cap_clear(caps); - - prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0); - - if (cap_set_proc(caps) < 0) { - pa_log("Failed to drop capabilities: %s", pa_cstrerror(errno)); - goto fail; - } - - r = 0; - -fail: + pa_assert_se(cap_set_proc(caps) == 0); cap_free(caps); - - return r; } #else @@ -144,9 +131,8 @@ int pa_limit_caps(void) { return 0; } -int pa_drop_caps(void) { +void pa_drop_caps(void) { pa_drop_root(); - return 0; } #endif diff --git a/src/daemon/caps.h b/src/daemon/caps.h index 4cd09578..91c88418 100644 --- a/src/daemon/caps.h +++ b/src/daemon/caps.h @@ -25,7 +25,7 @@ ***/ void pa_drop_root(void); +void pa_drop_caps(void); int pa_limit_caps(void); -int pa_drop_caps(void); #endif diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c index b77dd443..620a93a6 100644 --- a/src/daemon/cpulimit.c +++ b/src/daemon/cpulimit.c @@ -151,7 +151,7 @@ static void signal_handler(int sig) { } else if (phase == PHASE_SOFT) { write_err("Hard CPU time limit exhausted, terminating forcibly.\n"); - _exit(1); /* Forced exit */ + abort(); /* Forced exit */ } errno = saved_errno; diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index d664962e..e4cfb82b 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -38,7 +38,7 @@ ; module-idle-time = 20 ; scache-idle-time = 20 -; dl-search-path = @PA_DLSEARCHPATH@ +; dl-search-path = (depends on architecture) ; default-script-file = @PA_DEFAULT_CONFIG_FILE@ diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in index 597993c4..064a6cc9 100755 --- a/src/daemon/default.pa.in +++ b/src/daemon/default.pa.in @@ -37,7 +37,7 @@ load-sample-lazy pulse-hotplug /usr/share/sounds/startup3.wav #load-module module-pipe-sink ### Automatically load driver modules depending on the hardware available -.ifexists @PA_DLSEARCHPATH@/module-hal-detect@PA_SOEXT@ +.ifexists module-hal-detect@PA_SOEXT@ load-module module-hal-detect .else ### Alternatively use the static hardware detection module (for systems that @@ -46,7 +46,9 @@ load-module module-detect .endif ### Load several protocols +.ifexists module-esound-protocol-unix@PA_SOEXT@ load-module module-esound-protocol-unix +.endif load-module module-native-protocol-unix ### Network access (may be configured with paprefs, so leave this commented @@ -78,11 +80,6 @@ load-module module-suspend-on-idle ### Load X11 bell module #load-module module-x11-bell sample=x11-bell -### Publish connection data in the X11 root window -.ifexists @PA_DLSEARCHPATH@/module-x11-publish@PA_SOEXT@ -load-module module-x11-publish -.endif - ### Register ourselves in the X11 session manager # Deactivated by default, to avoid deadlock when PA is started as esd from gnome-session # Instead we load this via /etc/xdg/autostart/ and "pactl load-module" now @@ -91,8 +88,17 @@ load-module module-x11-publish ### Load additional modules from GConf settings. This can be configured with the paprefs tool. ### Please keep in mind that the modules configured by paprefs might conflict with manually ### loaded modules. -.ifexists @PA_DLSEARCHPATH@/module-gconf@PA_SOEXT@ +.ifexists module-gconf@PA_SOEXT@ +.nofail load-module module-gconf +.fail +.endif + +### Publish connection data in the X11 root window +.ifexists module-x11-publish@PA_SOEXT@ +.nofail +load-module module-x11-publish +.fail .endif ### Make some devices default diff --git a/src/daemon/main.c b/src/daemon/main.c index 7823180a..6b0c81da 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -242,7 +242,8 @@ static int change_user(void) { } set_env("USER", PA_SYSTEM_USER); - set_env("LOGNAME", PA_SYSTEM_GROUP); + set_env("USERNAME", PA_SYSTEM_USER); + set_env("LOGNAME", PA_SYSTEM_USER); set_env("HOME", PA_SYSTEM_RUNTIME_PATH); /* Relevant for pa_runtime_path() */ @@ -778,7 +779,7 @@ int main(int argc, char *argv[]) { c->disallow_module_loading = !!conf->disallow_module_loading; if (r < 0 && conf->fail) { - pa_log("failed to initialize daemon."); + pa_log("Failed to initialize daemon."); #ifdef HAVE_FORK if (conf->daemonize) pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); @@ -792,16 +793,19 @@ int main(int argc, char *argv[]) { } else { retval = 0; + + if (c->default_sink_name && + pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) { + pa_log_error("%s : Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name); + retval = !!conf->fail; + } + #ifdef HAVE_FORK if (conf->daemonize) pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); #endif - if (c->default_sink_name && - pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) { - pa_log_error("%s : Fatal error. Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name); - retval = 1; - } else { + if (!retval) { pa_log_info("Daemon startup complete."); if (pa_mainloop_run(mainloop, &retval) < 0) retval = 1; diff --git a/src/daemon/org.pulseaudio.policy b/src/daemon/org.pulseaudio.policy new file mode 100644 index 00000000..507a2cb1 --- /dev/null +++ b/src/daemon/org.pulseaudio.policy @@ -0,0 +1,52 @@ + + + + + + + + + The PulseAudio Project + http://pulseaudio.org/ + audio-card + + + Real-time scheduling for the PulseAudio daemon + System policy prevents PulseAudio from acquiring real-time scheduling. + + no + no + no + + + + + High-priority scheduling (negative Unix nice level) for the PulseAudio daemon + System policy prevents PulseAudio from acquiring high-priority scheduling. + + no + no + no + + + + diff --git a/src/daemon/polkit.c b/src/daemon/polkit.c index 362c5194..ce7c83e0 100644 --- a/src/daemon/polkit.c +++ b/src/daemon/polkit.c @@ -38,59 +38,6 @@ #include "polkit.h" -static pa_bool_t show_grant_dialog(const char *action_id) { - DBusError dbus_error; - DBusConnection *bus = NULL; - DBusMessage *m = NULL, *reply = NULL; - pa_bool_t r = FALSE; - uint32_t xid = 0; - int verdict; - - dbus_error_init(&dbus_error); - - if (!(bus = dbus_bus_get(DBUS_BUS_SESSION, &dbus_error))) { - pa_log_error("Cannot connect to session bus: %s", dbus_error.message); - goto finish; - } - - if (!(m = dbus_message_new_method_call("org.gnome.PolicyKit", "/org/gnome/PolicyKit/Manager", "org.gnome.PolicyKit.Manager", "ShowDialog"))) { - pa_log_error("Failed to allocate D-Bus message."); - goto finish; - } - - if (!(dbus_message_append_args(m, DBUS_TYPE_STRING, &action_id, DBUS_TYPE_UINT32, &xid, DBUS_TYPE_INVALID))) { - pa_log_error("Failed to append arguments to D-Bus message."); - goto finish; - } - - if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &dbus_error))) { - pa_log_warn("Failed to show grant dialog: %s", dbus_error.message); - goto finish; - } - - if (!(dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_BOOLEAN, &verdict, DBUS_TYPE_INVALID))) { - pa_log_warn("Malformed response from grant manager: %s", dbus_error.message); - goto finish; - } - - r = !!verdict; - -finish: - - if (bus) - dbus_connection_unref(bus); - - dbus_error_free(&dbus_error); - - if (m) - dbus_message_unref(m); - - if (reply) - dbus_message_unref(reply); - - return r; -} - int pa_polkit_check(const char *action_id) { int ret = -1; DBusError dbus_error; @@ -161,35 +108,32 @@ int pa_polkit_check(const char *action_id) { for (;;) { -#ifdef HAVE_POLKIT_CONTEXT_IS_CALLER_AUTHORIZED polkit_result = polkit_context_is_caller_authorized(context, action, caller, TRUE, &polkit_error); if (polkit_error_is_set(polkit_error)) { pa_log_error("Could not determine whether caller is authorized: %s", polkit_error_get_error_message(polkit_error)); goto finish; } -#else - - polkit_result = polkit_context_can_caller_do_action(context, action, caller); - -#endif if (polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH || polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_SESSION || polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_KEEP_ALWAYS || -#ifdef POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_ONE_SHOT polkit_result == POLKIT_RESULT_ONLY_VIA_ADMIN_AUTH_ONE_SHOT || -#endif polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH || polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_SESSION || - polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_ALWAYS -#ifdef POLKIT_RESULT_ONLY_VIA_SELF_AUTH_ONE_SHOT - || polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_ONE_SHOT -#endif + polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_KEEP_ALWAYS || + polkit_result == POLKIT_RESULT_ONLY_VIA_SELF_AUTH_ONE_SHOT ) { - if (show_grant_dialog(action_id)) - continue; + if (polkit_auth_obtain(action_id, 0, getpid(), &dbus_error)) { + polkit_result = POLKIT_RESULT_YES; + break; + } + + if (dbus_error_is_set(&dbus_error)) { + pa_log_error("Cannot obtain auth: %s", dbus_error.message); + goto finish; + } } break; diff --git a/src/modules/bt-proximity-helper.c b/src/modules/bt-proximity-helper.c index d80cc0c1..5f042c37 100644 --- a/src/modules/bt-proximity-helper.c +++ b/src/modules/bt-proximity-helper.c @@ -1,35 +1,29 @@ /* $Id$ */ -/*** - This file is part of PulseAudio. - - Copyright 2007 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - /* * Small SUID helper that allows us to ping a BT device. Borrows - * heavily from bluez-utils' l2ping, which is licensed as GPL2+, too + * heavily from bluez-utils' l2ping, which is licensed as GPL2+ * and comes with a copyright like this: * * Copyright (C) 2000-2001 Qualcomm Incorporated * Copyright (C) 2002-2003 Maxim Krasnyansky * Copyright (C) 2002-2007 Marcel Holtmann * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * */ #ifdef HAVE_CONFIG_H diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 696b6ea6..1ba2b48a 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -484,8 +484,7 @@ int pa__init(pa_module*m) { if (p > n_control || k) { pa_log("Too many control values passed, %lu expected.", n_control); - if (k) - pa_xfree(k); + pa_xfree(k); goto fail; } diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c index a9bd850b..600201b4 100644 --- a/src/modules/module-protocol-stub.c +++ b/src/modules/module-protocol-stub.c @@ -218,7 +218,7 @@ int pa__init(pa_module*m) { char tmp[PATH_MAX]; #if defined(USE_PROTOCOL_ESOUND) -#if defined(USE_PERUSER_ESOUND_SOCKET) +#if defined(USE_PER_USER_ESOUND_SOCKET) char esdsocketpath[PATH_MAX]; #else const char esdsocketpath[] = "/tmp/.esd/socket"; @@ -269,9 +269,10 @@ int pa__init(pa_module*m) { #if defined(USE_PROTOCOL_ESOUND) -#if defined(USE_PERUSER_ESOUND_SOCKET) +#if defined(USE_PER_USER_ESOUND_SOCKET) snprintf(esdsocketpath, sizeof(esdsocketpath), "/tmp/.esd-%lu/socket", (unsigned long) getuid()); #endif + pa_runtime_path(pa_modargs_get_value(ma, "socket", esdsocketpath), tmp, sizeof(tmp)); u->socket_path = pa_xstrdup(tmp); diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 2da2d134..5483be39 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -723,7 +723,7 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint3 goto fail; } - if (strcmp(name, u->sink_name)) + if (!u->sink_name || strcmp(name, u->sink_name)) return; pa_xfree(u->device_description); @@ -836,7 +836,7 @@ static void source_info_cb(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uin goto fail; } - if (strcmp(name, u->source_name)) + if (!u->source_name || strcmp(name, u->source_name)) return; pa_xfree(u->device_description); diff --git a/src/pulse/util.c b/src/pulse/util.c index d3ac9f66..b6f57b96 100644 --- a/src/pulse/util.c +++ b/src/pulse/util.c @@ -65,7 +65,7 @@ #include "util.h" char *pa_get_user_name(char *s, size_t l) { - char *p; + const char *p; char buf[1024]; #ifdef HAVE_PWD_H @@ -75,7 +75,10 @@ char *pa_get_user_name(char *s, size_t l) { pa_assert(s); pa_assert(l > 0); - if (!(p = getenv("USER")) && !(p = getenv("LOGNAME")) && !(p = getenv("USERNAME"))) { + if (!(p = (getuid() == 0 ? "root" : NULL)) && + !(p = getenv("USER")) && + !(p = getenv("LOGNAME")) && + !(p = getenv("USERNAME"))) { #ifdef HAVE_PWD_H #ifdef HAVE_GETPWUID_R diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h index c2c99888..ad3dca30 100644 --- a/src/pulsecore/atomic.h +++ b/src/pulsecore/atomic.h @@ -36,7 +36,7 @@ * On gcc >= 4.1 we use the builtin atomic functions. otherwise we use * libatomic_ops */ - +# #ifndef PACKAGE #error "Please include config.h before including this file!" #endif @@ -182,6 +182,235 @@ static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* n return result; } +#elif defined(ATOMIC_ARM_INLINE_ASM) + +/* + These should only be enabled if we have ARMv6 or better. +*/ + +typedef struct pa_atomic { + volatile int value; +} pa_atomic_t; + +#define PA_ATOMIC_INIT(v) { .value = (v) } + +static inline void pa_memory_barrier(void) { +#ifdef ATOMIC_ARM_MEMORY_BARRIER_ENABLED + asm volatile ("mcr p15, 0, r0, c7, c10, 5 @ dmb"); +#endif +} + +static inline int pa_atomic_load(const pa_atomic_t *a) { + pa_memory_barrier(); + return a->value; +} + +static inline void pa_atomic_store(pa_atomic_t *a, int i) { + a->value = i; + pa_memory_barrier(); +} + +/* Returns the previously set value */ +static inline int pa_atomic_add(pa_atomic_t *a, int i) { + unsigned long not_exclusive; + int new_val, old_val; + + pa_memory_barrier(); + do { + asm volatile ("ldrex %0, [%3]\n" + "add %2, %0, %4\n" + "strex %1, %2, [%3]\n" + : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val) + : "r" (&a->value), "Ir" (i) + : "cc"); + } while(not_exclusive); + pa_memory_barrier(); + + return old_val; +} + +/* Returns the previously set value */ +static inline int pa_atomic_sub(pa_atomic_t *a, int i) { + unsigned long not_exclusive; + int new_val, old_val; + + pa_memory_barrier(); + do { + asm volatile ("ldrex %0, [%3]\n" + "sub %2, %0, %4\n" + "strex %1, %2, [%3]\n" + : "=&r" (old_val), "=&r" (not_exclusive), "=&r" (new_val) + : "r" (&a->value), "Ir" (i) + : "cc"); + } while(not_exclusive); + pa_memory_barrier(); + + return old_val; +} + +static inline int pa_atomic_inc(pa_atomic_t *a) { + return pa_atomic_add(a, 1); +} + +static inline int pa_atomic_dec(pa_atomic_t *a) { + return pa_atomic_sub(a, 1); +} + +static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { + unsigned long not_equal, not_exclusive; + + pa_memory_barrier(); + do { + asm volatile ("ldrex %0, [%2]\n" + "subs %0, %0, %3\n" + "mov %1, %0\n" + "strexeq %0, %4, [%2]\n" + : "=&r" (not_exclusive), "=&r" (not_equal) + : "r" (&a->value), "Ir" (old_i), "r" (new_i) + : "cc"); + } while(not_exclusive && !not_equal); + pa_memory_barrier(); + + return !not_equal; +} + +typedef struct pa_atomic_ptr { + volatile unsigned long value; +} pa_atomic_ptr_t; + +#define PA_ATOMIC_PTR_INIT(v) { .value = (long) (v) } + +static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { + pa_memory_barrier(); + return (void*) a->value; +} + +static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { + a->value = (unsigned long) p; + pa_memory_barrier(); +} + +static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { + unsigned long not_equal, not_exclusive; + + pa_memory_barrier(); + do { + asm volatile ("ldrex %0, [%2]\n" + "subs %0, %0, %3\n" + "mov %1, %0\n" + "strexeq %0, %4, [%2]\n" + : "=&r" (not_exclusive), "=&r" (not_equal) + : "r" (&a->value), "Ir" (old_p), "r" (new_p) + : "cc"); + } while(not_exclusive && !not_equal); + pa_memory_barrier(); + + return !not_equal; +} + +#elif defined(ATOMIC_ARM_LINUX_HELPERS) + +/* See file arch/arm/kernel/entry-armv.S in your kernel sources for more + information about these functions. The arm kernel helper functions first + appeared in 2.6.16. + Apply --disable-atomic-arm-linux-helpers flag to confugure if you prefere + inline asm implementation or you have an obsolete Linux kernel. +*/ +/* Memory barrier */ +typedef void (__kernel_dmb_t)(void); +#define __kernel_dmb (*(__kernel_dmb_t *)0xffff0fa0) + +static inline void pa_memory_barrier(void) { +#ifndef ATOMIC_ARM_MEMORY_BARRIER_ENABLED + __kernel_dmb(); +#endif +} + +/* Atomic exchange (__kernel_cmpxchg_t contains memory barriers if needed) */ +typedef int (__kernel_cmpxchg_t)(int oldval, int newval, volatile int *ptr); +#define __kernel_cmpxchg (*(__kernel_cmpxchg_t *)0xffff0fc0) + +/* This is just to get rid of all warnings */ +typedef int (__kernel_cmpxchg_u_t)(unsigned long oldval, unsigned long newval, volatile unsigned long *ptr); +#define __kernel_cmpxchg_u (*(__kernel_cmpxchg_u_t *)0xffff0fc0) + +typedef struct pa_atomic { + volatile int value; +} pa_atomic_t; + +#define PA_ATOMIC_INIT(v) { .value = (v) } + +static inline int pa_atomic_load(const pa_atomic_t *a) { + pa_memory_barrier(); + return a->value; +} + +static inline void pa_atomic_store(pa_atomic_t *a, int i) { + a->value = i; + pa_memory_barrier(); +} + +/* Returns the previously set value */ +static inline int pa_atomic_add(pa_atomic_t *a, int i) { + int old_val; + do { + old_val = a->value; + } while(__kernel_cmpxchg(old_val, old_val + i, &a->value)); + return old_val; +} + +/* Returns the previously set value */ +static inline int pa_atomic_sub(pa_atomic_t *a, int i) { + int old_val; + do { + old_val = a->value; + } while(__kernel_cmpxchg(old_val, old_val - i, &a->value)); + return old_val; +} + +/* Returns the previously set value */ +static inline int pa_atomic_inc(pa_atomic_t *a) { + return pa_atomic_add(a, 1); +} + +/* Returns the previously set value */ +static inline int pa_atomic_dec(pa_atomic_t *a) { + return pa_atomic_sub(a, 1); +} + +/* Returns non-zero when the operation was successful. */ +static inline int pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) { + int failed = 1; + do { + failed = __kernel_cmpxchg(old_i, new_i, &a->value); + } while(failed && a->value == old_i); + return !failed; +} + +typedef struct pa_atomic_ptr { + volatile unsigned long value; +} pa_atomic_ptr_t; + +#define PA_ATOMIC_PTR_INIT(v) { .value = (unsigned long) (v) } + +static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) { + pa_memory_barrier(); + return (void*) a->value; +} + +static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) { + a->value = (unsigned long) p; + pa_memory_barrier(); +} + +static inline int pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) { + int failed = 1; + do { + failed = __kernel_cmpxchg_u((unsigned long) old_p, (unsigned long) new_p, &a->value); + } while(failed && a->value == old_p); + return !failed; +} + #else /* libatomic_ops based implementation */ diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 73b6619c..20510fc1 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -31,6 +31,7 @@ #include #include #include +#include #include @@ -1318,8 +1319,35 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b } else { const char *filename = cs+l+strspn(cs+l, whitespace); - *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE; - pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure"); + /* Search DL_SEARCH_PATH unless the filename is absolute */ + if (filename[0] == PA_PATH_SEP_CHAR) { + + *ifstate = access(filename, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE; + pa_log_debug("Checking for existance of '%s': %s", filename, *ifstate == IFSTATE_TRUE ? "success" : "failure"); + + } else { + const char *paths, *state = NULL; + char *p; + + if (!(paths = lt_dlgetsearchpath())) + return -1; + + while ((p = pa_split(paths, ":", &state))) { + char *pathname; + + pathname = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", p, filename); + pa_xfree(p); + + *ifstate = access(pathname, F_OK) == 0 ? IFSTATE_TRUE : IFSTATE_FALSE; + pa_log_debug("Checking for existance of '%s': %s", pathname, *ifstate == IFSTATE_TRUE ? "success" : "failure"); + + pa_xfree(pathname); + + if (*ifstate == IFSTATE_TRUE) + break; + } + } + } } else { pa_strbuf_printf(buf, "Invalid meta command: %s\n", cs); diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c index f5589664..c9d5632c 100644 --- a/src/pulsecore/hashmap.c +++ b/src/pulsecore/hashmap.c @@ -71,7 +71,7 @@ pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_f return h; } -static void remove(pa_hashmap *h, struct hashmap_entry *e) { +static void remove_entry(pa_hashmap *h, struct hashmap_entry *e) { pa_assert(h); pa_assert(e); @@ -103,7 +103,7 @@ void pa_hashmap_free(pa_hashmap*h, void (*free_func)(void *p, void *userdata), v while (h->first_entry) { if (free_func) free_func(h->first_entry->value, userdata); - remove(h, h->first_entry); + remove_entry(h, h->first_entry); } pa_xfree(h->data); @@ -182,7 +182,7 @@ void* pa_hashmap_remove(pa_hashmap *h, const void *key) { return NULL; data = e->value; - remove(h, e); + remove_entry(h, e); return data; } @@ -220,7 +220,7 @@ void* pa_hashmap_steal_first(pa_hashmap *h) { return NULL; data = h->first_entry->value; - remove(h, h->first_entry); + remove_entry(h, h->first_entry); return data; } diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index 60ab025c..1bf2cfbb 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -137,35 +139,47 @@ typedef int pa_bool_t; #define PA_PRETTY_FUNCTION "" #endif -#define pa_return_if_fail(expr) \ - do { \ - if (!(expr)) { \ - pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \ - return; \ - } \ - } while(0) - -#define pa_return_val_if_fail(expr, val) \ - do { \ - if (!(expr)) { \ - pa_log_debug("%s: Assertion <%s> failed.\n", PA_PRETTY_FUNCTION, #expr ); \ - return (val); \ - } \ - } while(0) +#define pa_return_if_fail(expr) \ + do { \ + if (PA_UNLIKELY(!(expr))) { \ + pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \ + return; \ + } \ + } while(FALSE) + +#define pa_return_val_if_fail(expr, val) \ + do { \ + if (PA_UNLIKELY(!(expr))) { \ + pa_log_debug("Assertion '%s' failed at %s:%u, function %s.\n", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \ + return (val); \ + } \ + } while(FALSE) #define pa_return_null_if_fail(expr) pa_return_val_if_fail(expr, NULL) -#define pa_assert assert - -#define pa_assert_not_reached() pa_assert(!"Should not be reached.") - -/* An assert which guarantees side effects of x */ +/* An assert which guarantees side effects of x, i.e. is never + * optimized away */ +#define pa_assert_se(expr) \ + do { \ + if (PA_UNLIKELY(!(expr))) { \ + pa_log_error("Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, PA_PRETTY_FUNCTION); \ + abort(); \ + } \ + } while (FALSE) + +/* An assert that may be optimized away by defining NDEBUG */ #ifdef NDEBUG -#define pa_assert_se(x) x +#define pa_assert(expr) do {} while (FALSE) #else -#define pa_assert_se(x) pa_assert(x) +#define pa_assert(expr) pa_assert_se(expr) #endif +#define pa_assert_not_reached() \ + do { \ + pa_log_error("Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, PA_PRETTY_FUNCTION); \ + abort(); \ + } while (FALSE) + #define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p)) #define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u)) diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 972e8e1a..59a4208f 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -72,7 +72,7 @@ #define RECORD_BUFFER_SECONDS (5) #define RECORD_BUFFER_FRAGMENTS (100) -#define MAX_CACHE_SAMPLE_SIZE (1024000) +#define MAX_CACHE_SAMPLE_SIZE (2048000) #define SCACHE_PREFIX "esound." diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 811cc805..0ac3ec1f 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -1193,6 +1193,7 @@ static void source_output_moved_cb(pa_source_output *o) { pa_tagstruct_putu32(t, s->index); pa_tagstruct_putu32(t, o->source->index); pa_tagstruct_puts(t, o->source->name); + pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED); pa_pstream_send_tagstruct(s->connection->pstream, t); } diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c index caa7df70..0816e76c 100644 --- a/src/tests/smoother-test.c +++ b/src/tests/smoother-test.c @@ -71,7 +71,7 @@ int main(int argc, char*argv[]) { u += 2; } - printf("%llu\t%llu\n", x/PA_USEC_PER_MSEC, pa_smoother_get(s, x)/PA_USEC_PER_MSEC); + printf("%llu\t%llu\n", (unsigned long long) (x/PA_USEC_PER_MSEC), (unsigned long long) (pa_smoother_get(s, x)/PA_USEC_PER_MSEC)); } pa_smoother_free(s); diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 4381d9d2..674eaee6 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -486,7 +486,7 @@ static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, i->name, i->type == PA_AUTOLOAD_SINK ? "sink" : "source", i->module, - i->argument); + i->argument ? i->argument : ""); } static void simple_callback(pa_context *c, int success, void *userdata) { diff --git a/src/utils/padsp.c b/src/utils/padsp.c index cb57ff8a..d3f034d0 100644 --- a/src/utils/padsp.c +++ b/src/utils/padsp.c @@ -2307,7 +2307,11 @@ fail: return ret; } +#ifdef sun +int ioctl(int fd, int request, ...) { +#else int ioctl(int fd, unsigned long request, ...) { +#endif fd_info *i; va_list args; void *argp; -- cgit From cdfcf6654cb826682812e9d1096dcfbac77900eb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 3 Apr 2008 13:40:55 +0000 Subject: - deprecate autoload stuff - allow setting of the requested latency of a sink input/source output before _put() is called - allow sinks/sources to have a "minimal" latency which applies to all requested latencies by sink inputs/source outputs - add new client library flags PA_STREAM_ADJUST_LATENCY, PA_STREAM_START_MUTED - allow client library to fill in 0 to buffer_attr fields - update module-alsa-source following module-alsa-sink - other cleanups and fixes git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2215 fefdeb5f-60dc-0310-8127-8f9354f1896f --- PROTOCOL | 11 +- src/modules/module-alsa-sink.c | 77 +++++++------- src/modules/module-alsa-source.c | 206 +++++++++++++++++++++++++++++--------- src/modules/oss-util.c | 4 +- src/modules/oss-util.h | 4 +- src/pulse/def.h | 73 ++++++++++++-- src/pulse/introspect.c | 36 ++++++- src/pulse/introspect.h | 210 +++++++++++++++++++++++++-------------- src/pulse/stream.c | 63 ++++++++---- src/pulsecore/cli-command.c | 16 ++- src/pulsecore/macro.h | 13 +++ src/pulsecore/memblockq.c | 24 ++--- src/pulsecore/module.c | 6 +- src/pulsecore/module.h | 4 +- src/pulsecore/protocol-native.c | 160 +++++++++++++++++++++++------ src/pulsecore/shm.c | 4 +- src/pulsecore/sink-input.c | 13 ++- src/pulsecore/sink-input.h | 2 +- src/pulsecore/sink.c | 4 + src/pulsecore/sink.h | 2 + src/pulsecore/source-output.c | 13 ++- src/pulsecore/source-output.h | 2 +- src/pulsecore/source.c | 5 + src/pulsecore/source.h | 2 + src/pulsecore/tagstruct.c | 10 +- src/pulsecore/tagstruct.h | 4 +- 26 files changed, 708 insertions(+), 260 deletions(-) diff --git a/PROTOCOL b/PROTOCOL index 40863fc2..74c08b49 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -79,7 +79,7 @@ New opcodes for notifications: PA_COMMAND_PLAYBACK_STREAM_MOVED PA_COMMAND_CAPTURE_STREAM_MOVED -### v13, implemented by >= 0.9.10 +### v13, implemented by >= 0.9.11 New fields for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM request at the end: @@ -113,7 +113,14 @@ New opcodes for proplist modifications New field for PA_COMMAND_PLAY_SAMPLE: proplist - + New field for PA_COMMAND_PLAY_SAMPLE response: idx + +New field for PA_COMMAND_CREATE_PLAYBACK_STREAM at the end: + + start_muted + +Buffer attributes for PA_COMMAND_CREATE_PLAYBACK_STREAM and +PA_COMMAND_CREATE_RECORD_STREAM may now be 0 for default values. diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 7e60cead..1dccf670 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -73,8 +73,8 @@ PA_MODULE_USAGE( "tsched_buffer_watermark="); #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (3*PA_USEC_PER_SEC) -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) +#define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC) struct userdata { pa_core *core; @@ -325,7 +325,7 @@ static int unix_write(struct userdata *u) { } static int update_smoother(struct userdata *u) { - snd_pcm_sframes_t delay; + snd_pcm_sframes_t delay = 0; int64_t frames; int err; pa_usec_t now1, now2; @@ -334,6 +334,7 @@ static int update_smoother(struct userdata *u) { pa_assert(u->pcm_handle); /* Let's update the time smoother */ + snd_pcm_avail_update(u->pcm_handle); if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { @@ -441,6 +442,31 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { return usec; } +static void update_hwbuf_unused_frames(struct userdata *u) { + pa_usec_t usec; + size_t b; + + pa_assert(u); + + if ((usec = pa_sink_get_requested_latency(u->sink)) <= 0) { + /* Use the full buffer if noone asked us for anything + * specific */ + u->hwbuf_unused_frames = 0; + return; + } + + b = pa_usec_to_bytes(usec, &u->sink->sample_spec); + + /* We need at least one sample in our buffer */ + + if (PA_UNLIKELY(b < u->frame_size)) + b = u->frame_size; + + u->hwbuf_unused_frames = + PA_LIKELY(b < u->hwbuf_size) ? + ((u->hwbuf_size - b) / u->frame_size) : 0; +} + static int update_sw_params(struct userdata *u) { size_t avail_min; int err; @@ -465,6 +491,8 @@ static int update_sw_params(struct userdata *u) { return err; } + update_hwbuf_unused_frames(u); + return 0; } @@ -535,29 +563,6 @@ fail: return -1; } -static void update_hwbuf_unused_frames(struct userdata *u) { - pa_usec_t usec; - size_t b; - - pa_assert(u); - - if ((usec = pa_sink_get_requested_latency(u->sink)) <= 0) { - /* Use the full buffer if noone asked us for anything - * specific */ - u->hwbuf_unused_frames = 0; - return; - } - - b = pa_usec_to_bytes(usec, &u->sink->sample_spec); - - /* We need at least one sample in our buffer */ - - if (PA_UNLIKELY(b < u->frame_size)) - b = u->frame_size; - - u->hwbuf_unused_frames = PA_LIKELY(b < u->hwbuf_size) ? ((u->hwbuf_size - b) / u->frame_size) : 0; -} - static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; @@ -608,13 +613,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse break; - case PA_SINK_MESSAGE_ADD_INPUT: - case PA_SINK_MESSAGE_REMOVE_INPUT: - case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: { - int r = pa_sink_process_msg(o, code, data, offset, chunk); - update_hwbuf_unused_frames(u); - return r; - } +/* case PA_SINK_MESSAGE_ADD_INPUT: */ +/* case PA_SINK_MESSAGE_REMOVE_INPUT: */ +/* case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: { */ +/* int r = pa_sink_process_msg(o, code, data, offset, chunk); */ +/* update_hwbuf_unused_frames(u); */ +/* return r; */ +/* } */ } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -703,6 +708,7 @@ static int sink_set_volume_cb(pa_sink *s) { } u->hw_dB_supported = FALSE; + } alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; @@ -776,7 +782,7 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); - update_hwbuf_unused_frames(u); +/* update_hwbuf_unused_frames(u); */ for (;;) { int ret; @@ -1174,6 +1180,9 @@ int pa__init(pa_module*m) { u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0; + if (!use_tsched) + u->sink->min_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", nfrags, (long unsigned) u->fragment_size, (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index b2d0d43d..af20f814 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -92,6 +92,8 @@ struct userdata { snd_mixer_t *mixer_handle; snd_mixer_elem_t *mixer_elem; long hw_volume_max, hw_volume_min; + long hw_dB_max, hw_dB_min; + pa_bool_t hw_dB_supported; size_t frame_size, fragment_size, hwbuf_size, tsched_watermark; unsigned nfragments; @@ -288,25 +290,28 @@ static int unix_read(struct userdata *u) { } static int update_smoother(struct userdata *u) { - snd_pcm_status_t *status; + snd_pcm_sframes_t delay = 0; int64_t frames; int err; + pa_usec_t now1, now2; pa_assert(u); pa_assert(u->pcm_handle); - snd_pcm_status_alloca(&status); - /* Let's update the time smoother */ - if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { + snd_pcm_avail_update(u->pcm_handle); + + if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { pa_log("Failed to get delay: %s", snd_strerror(err)); return -1; } - frames = u->frame_index + snd_pcm_status_get_delay(status); + frames = u->frame_index + delay; - pa_smoother_put(u->smoother, pa_rtclock_usec(), pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec)); + now1 = pa_rtclock_usec(); + now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec); + pa_smoother_put(u->smoother, now1, now2); return 0; } @@ -373,7 +378,7 @@ static int suspend(struct userdata *u) { } static pa_usec_t hw_sleep_time(struct userdata *u) { - pa_usec_t usec; + pa_usec_t wm, usec; pa_assert(u); @@ -382,11 +387,17 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { if (usec <= 0) usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); - if (usec >= u->tsched_watermark) - usec -= u->tsched_watermark; + pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); + + wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec); + + if (usec >= wm) + usec -= wm; else usec /= 2; + pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); + return usec; } @@ -470,7 +481,6 @@ static int unsuspend(struct userdata *u) { /* FIXME: We need to reload the volume somehow */ snd_pcm_start(u->pcm_handle); - pa_smoother_resume(u->smoother, pa_rtclock_usec()); pa_log_info("Resumed successfully..."); @@ -568,18 +578,24 @@ static int source_get_volume_cb(pa_source *s) { pa_assert(u->mixer_elem); for (i = 0; i < s->sample_spec.channels; i++) { - long set_vol, vol; + long alsa_vol; pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i])); - if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &vol)) < 0) - goto fail; + if (u->hw_dB_supported) { - set_vol = (long) roundf(((float) s->volume.values[i] * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; + if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) >= 0) { + s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); + continue; + } + + u->hw_dB_supported = FALSE; + } - /* Try to avoid superfluous volume changes */ - if (set_vol != vol) - s->volume.values[i] = (pa_volume_t) roundf(((float) (vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); + if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0) + goto fail; + + s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); } return 0; @@ -587,8 +603,6 @@ static int source_get_volume_cb(pa_source *s) { fail: pa_log_error("Unable to read volume: %s", snd_strerror(err)); - s->get_volume = NULL; - s->set_volume = NULL; return -1; } @@ -604,17 +618,36 @@ static int source_set_volume_cb(pa_source *s) { long alsa_vol; pa_volume_t vol; + pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i])); - vol = s->volume.values[i]; + vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM); + + if (u->hw_dB_supported) { + alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100); + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max); + + + if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, -1)) >= 0) { + + if (snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) + s->volume.values[i] = pa_sw_volume_from_dB(alsa_vol / 100.0); + + continue; + } + + u->hw_dB_supported = FALSE; + } - if (vol > PA_VOLUME_NORM) - vol = PA_VOLUME_NORM; alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; + alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0) goto fail; + + if (snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol) >= 0) + s->volume.values[i] = (pa_volume_t) roundf(((float) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) / (u->hw_volume_max - u->hw_volume_min)); } return 0; @@ -622,8 +655,6 @@ static int source_set_volume_cb(pa_source *s) { fail: pa_log_error("Unable to set volume: %s", snd_strerror(err)); - s->get_volume = NULL; - s->set_volume = NULL; return -1; } @@ -636,9 +667,6 @@ static int source_get_mute_cb(pa_source *s) { if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) { pa_log_error("Unable to get switch: %s", snd_strerror(err)); - - s->get_mute = NULL; - s->set_mute = NULL; return -1; } @@ -656,15 +684,20 @@ static int source_set_mute_cb(pa_source *s) { if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) { pa_log_error("Unable to set switch: %s", snd_strerror(err)); - - s->get_mute = NULL; - s->set_mute = NULL; return -1; } return 0; } +static void source_update_requested_latency_cb(pa_source *s) { + struct userdata *u = s->userdata; + + pa_assert(u); + + update_sw_params(u); +} + static void thread_func(void *userdata) { struct userdata *u = userdata; @@ -694,10 +727,11 @@ static void thread_func(void *userdata) { goto fail; } - if (update_smoother(u) < 0) - goto fail; + if (work_done) + if (update_smoother(u) < 0) + goto fail; - if (u->use_tsched && work_done) { + if (u->use_tsched) { pa_usec_t usec, cusec; /* OK, the capture buffer is now empty, let's @@ -716,6 +750,10 @@ static void thread_func(void *userdata) { /* We don't trust the conversion, so we wake up whatever comes first */ pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); } + } else if (u->use_tsched) { + + /* OK, we're in an invalid state, let's disable our timers */ + pa_rtpoll_set_timer_disabled(u->rtpoll); } /* Hmm, nothing to do. Let's sleep */ @@ -797,7 +835,7 @@ int pa__init(pa_module*m) { const char *dev_id; pa_sample_spec ss; pa_channel_map map; - uint32_t nfrags, frag_size, tsched_size, tsched_watermark; + uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark; snd_pcm_uframes_t period_frames, tsched_frames; size_t frame_size; snd_pcm_info_t *pcm_info = NULL; @@ -846,6 +884,7 @@ int pa__init(pa_module*m) { goto fail; } + hwbuf_size = frag_size * nfrags; period_frames = frag_size/frame_size; tsched_frames = tsched_size/frame_size; @@ -874,6 +913,7 @@ int pa__init(pa_module*m) { u->rtpoll = pa_rtpoll_new(); u->alsa_rtpoll_item = NULL; pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE); pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); @@ -929,9 +969,6 @@ int pa__init(pa_module*m) { goto fail; } - if (update_sw_params(u) < 0) - goto fail; - /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); @@ -996,6 +1033,7 @@ int pa__init(pa_module*m) { } u->source->parent.process_msg = source_process_msg; + u->source->update_requested_latency = source_update_requested_latency_cb; u->source->userdata = u; pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); @@ -1007,24 +1045,85 @@ int pa__init(pa_module*m) { u->hwbuf_size = u->fragment_size * nfrags; u->tsched_watermark = tsched_watermark; u->frame_index = 0; + u->hw_dB_supported = FALSE; + u->hw_dB_min = u->hw_dB_max = 0; + u->hw_volume_min = u->hw_volume_max = 0; + + if (!use_tsched) + u->source->min_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + + pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", + nfrags, (long unsigned) u->fragment_size, + (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); + + if (use_tsched) + pa_log_info("Time scheduling watermark is %0.2fms", + (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC); - pa_log_info("Using %u fragments of size %lu bytes.", nfrags, (long unsigned) u->fragment_size); + if (update_sw_params(u) < 0) + goto fail; if (u->mixer_handle) { pa_assert(u->mixer_elem); if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) - if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0) { - u->source->get_volume = source_get_volume_cb; - u->source->set_volume = source_set_volume_cb; - snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max); - u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL; + if (pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0 && + snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) >= 0) { + + pa_bool_t suitable = TRUE; + + pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max); + + if (u->hw_volume_min > u->hw_volume_max) { + + pa_log_info("Minimal volume %li larger than maximum volume %li. Strange stuff Falling back to software volume control.", u->hw_volume_min, u->hw_volume_max); + suitable = FALSE; + + } else if (u->hw_volume_max - u->hw_volume_min < 3) { + + pa_log_info("Device has less than 4 volume levels. Falling back to software volume control."); + suitable = FALSE; + + } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { + + pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); + + /* Let's see if this thing actually is useful for muting */ + if (u->hw_dB_min > -6000) { + pa_log_info("Device cannot attenuate for more than -60 dB (only %0.2f dB supported), falling back to software volume control.", ((double) u->hw_dB_min) / 100); + + suitable = FALSE; + } else if (u->hw_dB_max < 0) { + + pa_log_info("Device is still attenuated at maximum volume setting (%0.2f dB is maximum). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_max) / 100); + suitable = FALSE; + + } else if (u->hw_dB_min >= u->hw_dB_max) { + + pa_log_info("Minimal dB (%0.2f) larger or equal to maximum dB (%0.2f). Strange stuff. Falling back to software volume control.", ((double) u->hw_dB_min) / 100, ((double) u->hw_dB_max) / 100); + suitable = FALSE; + + } else + u->hw_dB_supported = TRUE; + } + + if (suitable) { + u->source->get_volume = source_get_volume_cb; + u->source->set_volume = source_set_volume_cb; + u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0); + pa_log_info("Using hardware volume control. %s dB scale.", u->hw_dB_supported ? "Using" : "Not using"); + + } else { + pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); + pa_alsa_0dB_capture(u->mixer_elem); + } } + if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) { u->source->get_mute = source_get_mute_cb; u->source->set_mute = source_set_mute_cb; - u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL; + u->source->flags |= PA_SOURCE_HW_MUTE_CTRL; } u->mixer_fdl = pa_alsa_fdlist_new(); @@ -1044,10 +1143,21 @@ int pa__init(pa_module*m) { goto fail; } /* Get initial mixer settings */ - if (u->source->get_volume) - u->source->get_volume(u->source); - if (u->source->get_mute) - u->source->get_mute(u->source); + if (data.volume_is_set) { + if (u->source->set_volume) + u->source->set_volume(u->source); + } else { + if (u->source->get_volume) + u->source->get_volume(u->source); + } + + if (data.muted_is_set) { + if (u->source->set_mute) + u->source->set_mute(u->source); + } else { + if (u->source->get_mute) + u->source->get_mute(u->source); + } pa_source_put(u->source); diff --git a/src/modules/oss-util.c b/src/modules/oss-util.c index 9598feee..e29f0eda 100644 --- a/src/modules/oss-util.c +++ b/src/modules/oss-util.c @@ -251,7 +251,7 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) { return 0; } -int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume) { +int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) { char cv[PA_CVOLUME_SNPRINT_MAX]; unsigned vol; @@ -273,7 +273,7 @@ int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *v return 0; } -int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) { +int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) { char cv[PA_CVOLUME_SNPRINT_MAX]; unsigned vol; pa_volume_t l, r; diff --git a/src/modules/oss-util.h b/src/modules/oss-util.h index 259a622a..8fea805c 100644 --- a/src/modules/oss-util.h +++ b/src/modules/oss-util.h @@ -33,8 +33,8 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss); int pa_oss_set_fragments(int fd, int frags, int frag_size); -int pa_oss_set_volume(int fd, long mixer, const pa_sample_spec *ss, const pa_cvolume *volume); -int pa_oss_get_volume(int fd, int mixer, const pa_sample_spec *ss, pa_cvolume *volume); +int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume); +int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume); int pa_oss_get_hw_description(const char *dev, char *name, size_t l); diff --git a/src/pulse/def.h b/src/pulse/def.h index edda792a..ae766603 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -210,16 +210,68 @@ typedef enum pa_stream_flags { * on older servers. \since * 0.9.8 */ PA_STREAM_PEAK_DETECT = 2048, /**< Find peaks instead of - * resampling. \since 0.9.9 */ + * resampling. \since 0.9.11 */ + + PA_STREAM_START_MUTED = 4096, /**< Create in muted state. \since 0.9.11 */ + + + PA_STREAM_ADJUST_LATENCY = 8192, /**< Try to adjust the latency of + * the sink/source based on the + * requested buffer metrics and + * adjust buffer metrics + * accordingly. \since 0.9.11 */ } pa_stream_flags_t; /** Playback and record buffer metrics */ typedef struct pa_buffer_attr { - uint32_t maxlength; /**< Maximum length of the buffer */ - uint32_t tlength; /**< Playback only: target length of the buffer. The server tries to assure that at least tlength bytes are always available in the buffer */ - uint32_t prebuf; /**< Playback only: pre-buffering. The server does not start with playback before at least prebug bytes are available in the buffer */ - uint32_t minreq; /**< Playback only: minimum request. The server does not request less than minreq bytes from the client, instead waints until the buffer is free enough to request more bytes at once */ - uint32_t fragsize; /**< Recording only: fragment size. The server sends data in blocks of fragsize bytes size. Large values deminish interactivity with other operations on the connection context but decrease control overhead. */ + uint32_t maxlength; /**< Maximum length of the + * buffer. Setting this to 0 will + * initialize this to the maximum value + * supported by server, which is + * recommended. */ + uint32_t tlength; /**< Playback only: target length of the + * buffer. The server tries to assure + * that at least tlength bytes are always + * available in the buffer. It is + * recommended to set this to 0, which + * will initialize this to a value that + * is deemed sensible by the + * server. However, this value will + * default to something like 2s, i.e. for + * applications that have specific + * latency requirements this value should + * be set to the maximum latency that the + * application can deal with. */ + uint32_t prebuf; /**< Playback only: pre-buffering. The + * server does not start with playback + * before at least prebug bytes are + * available in the buffer. It is + * recommended to set this to 0, which + * will initialize this to the same value + * as tlength, whatever that may be. */ + uint32_t minreq; /**< Playback only: minimum request. The + * server does not request less than + * minreq bytes from the client, instead + * waits until the buffer is free enough + * to request more bytes at once. It is + * recommended to set this to 0, which + * will initialize this to a value that + * is deemed sensible by the server. */ + uint32_t fragsize; /**< Recording only: fragment size. The + * server sends data in blocks of + * fragsize bytes size. Large values + * deminish interactivity with other + * operations on the connection context + * but decrease control overhead. It is + * recommended to set this to 0, which + * will initialize this to a value that + * is deemed sensible by the + * server. However, this value will + * default to something like 2s, i.e. for + * applications that have specific + * latency requirements this value should + * be set to the maximum latency that the + * application can deal with. */ } pa_buffer_attr; /** Error values as used by pa_context_errno(). Use pa_strerror() to convert these values to human readable strings */ @@ -299,7 +351,9 @@ typedef enum pa_subscription_event_type { * source_usec+buffer_usec+transport_usec-sink_usec. (Take care of * sign issues!) When connected to a monitor source sink_usec contains * the latency of the owning sink. The two latency estimations - * described here are implemented in pa_stream_get_latency().*/ + * described here are implemented in pa_stream_get_latency(). Please + * note that this structure can be extended as part of evolutionary + * API updates at any time in any new release.*/ typedef struct pa_timing_info { struct timeval timestamp; /**< The time when this timing info structure was current */ int synchronized_clocks; /**< Non-zero if the local and the @@ -346,6 +400,11 @@ typedef struct pa_timing_info { * want to use it. Consider using * PA_SEEK_RELATIVE_ON_READ * instead. \since 0.8 */ + + pa_usec_t max_sink_usec; /**< The static configure latency for + * the sink. \since 0.9.10 */ + pa_usec_t max_source_usec; /**< The static configure latency for + * the source. \since 0.9.10 */ } pa_timing_info; /** A structure for the spawn api. This may be used to integrate auto diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 4b282bdb..633ee558 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -149,6 +149,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P while (!pa_tagstruct_eof(t)) { pa_sink_info i; + pa_bool_t mute = FALSE; memset(&i, 0, sizeof(i)); i.proplist = pa_proplist_new(); @@ -160,7 +161,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || pa_tagstruct_getu32(t, &i.owner_module) < 0 || pa_tagstruct_get_cvolume(t, &i.volume) < 0 || - pa_tagstruct_get_boolean(t, &i.mute) < 0 || + pa_tagstruct_get_boolean(t, &mute) < 0 || pa_tagstruct_getu32(t, &i.monitor_source) < 0 || pa_tagstruct_gets(t, &i.monitor_source_name) < 0 || pa_tagstruct_get_usec(t, &i.latency) < 0 || @@ -173,6 +174,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P goto finish; } + i.mute = (int) mute; i.flags = (pa_sink_flags_t) flags; if (o->callback) { @@ -266,6 +268,7 @@ 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; + pa_bool_t mute = FALSE; memset(&i, 0, sizeof(i)); i.proplist = pa_proplist_new(); @@ -277,7 +280,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 || pa_tagstruct_getu32(t, &i.owner_module) < 0 || pa_tagstruct_get_cvolume(t, &i.volume) < 0 || - pa_tagstruct_get_boolean(t, &i.mute) < 0 || + pa_tagstruct_get_boolean(t, &mute) < 0 || pa_tagstruct_getu32(t, &i.monitor_of_sink) < 0 || pa_tagstruct_gets(t, &i.monitor_of_sink_name) < 0 || pa_tagstruct_get_usec(t, &i.latency) < 0 || @@ -290,6 +293,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; } + i.mute = (int) mute; i.flags = (pa_source_flags_t) flags; if (o->callback) { @@ -464,17 +468,20 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_module_info i; + pa_bool_t auto_unload = FALSE; memset(&i, 0, sizeof(i)); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_gets(t, &i.argument) < 0 || pa_tagstruct_getu32(t, &i.n_used) < 0 || - pa_tagstruct_get_boolean(t, &i.auto_unload) < 0) { + pa_tagstruct_get_boolean(t, &auto_unload) < 0) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } + i.auto_unload = (int) auto_unload; + if (o->callback) { pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback; cb(o->context, &i, 0, o->userdata); @@ -540,6 +547,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm while (!pa_tagstruct_eof(t)) { pa_sink_input_info i; + pa_bool_t mute = FALSE; memset(&i, 0, sizeof(i)); i.proplist = pa_proplist_new(); @@ -556,7 +564,7 @@ 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, &mute) < 0) || (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) { pa_context_fail(o->context, PA_ERR_PROTOCOL); @@ -564,6 +572,8 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm goto finish; } + i.mute = (int) mute; + if (o->callback) { pa_sink_input_info_cb_t cb = (pa_sink_input_info_cb_t) o->callback; cb(o->context, &i, 0, o->userdata); @@ -961,6 +971,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_sample_info i; + pa_bool_t lazy = FALSE; memset(&i, 0, sizeof(i)); i.proplist = pa_proplist_new(); @@ -972,7 +983,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, pa_tagstruct_get_sample_spec(t, &i.sample_spec) < 0 || 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_get_boolean(t, &lazy) < 0 || pa_tagstruct_gets(t, &i.filename) < 0 || (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) { @@ -980,6 +991,8 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; } + i.lazy = (int) lazy; + if (o->callback) { pa_sample_info_cb_t cb = (pa_sample_info_cb_t) o->callback; cb(o->context, &i, 0, o->userdata); @@ -1192,6 +1205,8 @@ finish: pa_operation_unref(o); } +PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server."); + pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) { pa_tagstruct *t; pa_operation *o; @@ -1216,6 +1231,8 @@ pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *na return o; } +PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server."); + pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) { pa_tagstruct *t; pa_operation *o; @@ -1238,10 +1255,15 @@ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, return o; } + +PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Autoload will no longer be implemented by future versions of the PulseAudio server."); + pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) { return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata); } +PA_WARN_REFERENCE(pa_context_add_autoload, "Autoload will no longer be implemented by future versions of the PulseAudio server."); + pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) { pa_operation *o; pa_tagstruct *t; @@ -1268,6 +1290,8 @@ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autolo return o; } +PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server."); + pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) { pa_operation *o; pa_tagstruct *t; @@ -1291,6 +1315,8 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name return o; } +PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server."); + pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) { pa_operation *o; pa_tagstruct *t; diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 91f738df..bc78996f 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -212,7 +212,11 @@ PA_C_DECL_BEGIN #define PA_PORT_ANALOG_5_1 "analog-5-1" #define PA_PORT_ANALOG_4_0 "analog-4-0" -/** Stores information about sinks */ +/** @{ \name Sinks */ + +/** Stores information about sinks. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. */ typedef struct pa_sink_info { const char *name; /**< Name of the sink */ uint32_t index; /**< Index of the sink */ @@ -224,10 +228,11 @@ typedef struct pa_sink_info { int mute; /**< Mute switch of the sink \since 0.8 */ uint32_t monitor_source; /**< Index of the monitor source connected to this sink */ const char *monitor_source_name; /**< The name of the monitor source */ - pa_usec_t latency; /**< Length of filled playback buffer of this sink */ + pa_usec_t latency; /**< Length of queued audio in the output buffer. */ 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_proplist *proplist; /**< Property list \since 0.9.11 */ + pa_usec_t max_latency; /**< The static latency this device has been configured to. \since 0.9.11 */ } pa_sink_info; /** Callback prototype for pa_context_get_sink_info_by_name() and friends */ @@ -242,7 +247,31 @@ pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_s /** Get the complete sink list */ pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata); -/** Stores information about sources */ +/** Set the volume of a sink device specified by its index */ +pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the volume of a sink device specified by its name */ +pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a sink device specified by its index \since 0.8 */ +pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a sink device specified by its name \since 0.8 */ +pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); + +/** Suspend/Resume a sink. \since 0.9.7 */ +pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata); + +/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */ +pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); + +/** @} */ + +/** @{ \name Sources */ + +/** Stores information about sources. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. */ typedef struct pa_source_info { const char *name; /**< Name of the source */ uint32_t index; /**< Index of the source */ @@ -258,6 +287,7 @@ typedef struct pa_source_info { 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_usec_t max_latency; /**< The static latency this device has been configured to. \since 0.9.11 */ } pa_source_info; /** Callback prototype for pa_context_get_source_info_by_name() and friends */ @@ -272,7 +302,25 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa /** Get the complete source list */ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata); -/** Server information */ +/** Set the volume of a source device specified by its index \since 0.8 */ +pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the volume of a source device specified by its name \since 0.8 */ +pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a source device specified by its index \since 0.8 */ +pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a source device specified by its name \since 0.8 */ +pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); + +/** @} */ + +/** @{ \name Server */ + +/** Server information. Please note that this structure can be + * extended as part of evolutionary API updates at any time in any new + * release. */ typedef struct pa_server_info { const char *user_name; /**< User name of the daemon process */ const char *host_name; /**< Host name the daemon is running on */ @@ -290,7 +338,13 @@ typedef void (*pa_server_info_cb_t) (pa_context *c, const pa_server_info*i, void /** Get some information about the server */ pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb, void *userdata); -/** Stores information about modules */ +/** @} */ + +/** @{ \name Modules */ + +/** Stores information about modules. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. */ typedef struct pa_module_info { uint32_t index; /**< Index of the module */ const char*name, /**< Name of the module */ @@ -308,7 +362,22 @@ pa_operation* pa_context_get_module_info(pa_context *c, uint32_t idx, pa_module_ /** Get the complete list of currently loaded modules */ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t cb, void *userdata); -/** Stores information about clients */ +/** Callback prototype for pa_context_load_module() */ +typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata); + +/** Load a module. \since 0.5 */ +pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata); + +/** Unload a module. \since 0.5 */ +pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); + +/** @} */ + +/** @{ \name Clients */ + +/** Stores information about clients. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. */ typedef struct pa_client_info { uint32_t index; /**< Index of this client */ const char *name; /**< Name of this client */ @@ -326,7 +395,16 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ /** Get the complete client list */ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata); -/** Stores information about sink inputs */ +/** Kill a client. \since 0.5 */ +pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); + +/** @} */ + +/** @{ \name Sink Inputs */ + +/** Stores information about sink inputs. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. */ typedef struct pa_sink_input_info { uint32_t index; /**< Index of the sink input */ const char *name; /**< Name of the sink input */ @@ -353,7 +431,28 @@ pa_operation* pa_context_get_sink_input_info(pa_context *c, uint32_t idx, pa_sin /** Get the complete sink input list */ pa_operation* pa_context_get_sink_input_info_list(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata); -/** Stores information about source outputs */ +/** Move the specified sink input to a different sink. \since 0.9.5 */ +pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata); + +/** Move the specified sink input to a different sink. \since 0.9.5 */ +pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata); + +/** Set the volume of a sink input stream */ +pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); + +/** Set the mute switch of a sink input stream \since 0.9.7 */ +pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); + +/** Kill a sink input. \since 0.5 */ +pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); + +/** @} */ + +/** @{ \name Source Outputs */ + +/** Stores information about source outputs. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. */ typedef struct pa_source_output_info { uint32_t index; /**< Index of the sink input */ const char *name; /**< Name of the sink input */ @@ -378,37 +477,28 @@ pa_operation* pa_context_get_source_output_info(pa_context *c, uint32_t idx, pa_ /** Get the complete list of source outputs */ pa_operation* pa_context_get_source_output_info_list(pa_context *c, pa_source_output_info_cb_t cb, void *userdata); -/** Set the volume of a sink device specified by its index */ -pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); - -/** Set the volume of a sink device specified by its name */ -pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); - -/** Set the mute switch of a sink device specified by its index \since 0.8 */ -pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); - -/** Set the mute switch of a sink device specified by its name \since 0.8 */ -pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); +/** Move the specified source output to a different source. \since 0.9.5 */ +pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata); -/** Set the volume of a sink input stream */ -pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); +/** Move the specified source output to a different source. \since 0.9.5 */ +pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata); -/** Set the mute switch of a sink input stream \since 0.9.7 */ -pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); +/** Suspend/Resume a source. \since 0.9.7 */ +pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata); -/** Set the volume of a source device specified by its index \since 0.8 */ -pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); +/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */ +pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); -/** Set the volume of a source device specified by its name \since 0.8 */ -pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); +/** Kill a source output. \since 0.5 */ +pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); -/** Set the mute switch of a source device specified by its index \since 0.8 */ -pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); +/** @} */ -/** Set the mute switch of a source device specified by its name \since 0.8 */ -pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); +/** @{ \name Statistics */ -/** Memory block statistics */ +/** Memory block statistics. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. */ typedef struct pa_stat_info { uint32_t memblock_total; /**< Currently allocated memory blocks */ uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */ @@ -423,7 +513,13 @@ typedef void (*pa_stat_info_cb_t) (pa_context *c, const pa_stat_info *i, void *u /** Get daemon memory block statistics */ pa_operation* pa_context_stat(pa_context *c, pa_stat_info_cb_t cb, void *userdata); -/** Stores information about sample cache entries */ +/** @} */ + +/** @{ \name Cached Samples */ + +/** Stores information about sample cache entries. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. */ typedef struct pa_sample_info { uint32_t index; /**< Index of this entry */ const char *name; /**< Name of this entry */ @@ -449,23 +545,11 @@ pa_operation* pa_context_get_sample_info_by_index(pa_context *c, uint32_t idx, p /** Get the complete list of samples stored in the daemon. */ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t cb, void *userdata); -/** Kill a client. \since 0.5 */ -pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); - -/** Kill a sink input. \since 0.5 */ -pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); - -/** Kill a source output. \since 0.5 */ -pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); - -/** Callback prototype for pa_context_load_module() and pa_context_add_autoload() */ -typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata); +/** @} */ -/** Load a module. \since 0.5 */ -pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata); +/** \cond fulldocs */ -/** Unload a module. \since 0.5 */ -pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); +/** @{ \name Autoload Entries */ /** Type of an autoload entry. \since 0.5 */ typedef enum pa_autoload_type { @@ -473,7 +557,9 @@ typedef enum pa_autoload_type { PA_AUTOLOAD_SOURCE = 1 } pa_autoload_type_t; -/** Stores information about autoload entries. \since 0.5 */ +/** Stores information about autoload entries. Please note that this structure + * can be extended as part of evolutionary API updates at any time in + * any new release. \since 0.5 */ typedef struct pa_autoload_info { uint32_t index; /**< Index of this autoload entry */ const char *name; /**< Name of the sink or source */ @@ -503,29 +589,9 @@ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name /** Remove an autoload entry. \since 0.6 */ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata); -/** Move the specified sink input to a different sink. \since 0.9.5 */ -pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, char *sink_name, pa_context_success_cb_t cb, void* userdata); - -/** Move the specified sink input to a different sink. \since 0.9.5 */ -pa_operation* pa_context_move_sink_input_by_index(pa_context *c, uint32_t idx, uint32_t sink_idx, pa_context_success_cb_t cb, void* userdata); +/** @} */ -/** Move the specified source output to a different source. \since 0.9.5 */ -pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx, char *source_name, pa_context_success_cb_t cb, void* userdata); - -/** Move the specified source output to a different source. \since 0.9.5 */ -pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata); - -/** Suspend/Resume a sink. \since 0.9.7 */ -pa_operation* pa_context_suspend_sink_by_name(pa_context *c, char *sink_name, int suspend, pa_context_success_cb_t cb, void* userdata); - -/** Suspend/Resume a sink. If idx is PA_INVALID_INDEX all sinks will be suspended. \since 0.9.7 */ -pa_operation* pa_context_suspend_sink_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); - -/** Suspend/Resume a source. \since 0.9.7 */ -pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata); - -/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */ -pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); +/** \endcond */ PA_C_DECL_END diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 321f2228..fb965238 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -292,7 +292,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u pa_stream *s; uint32_t channel; const char *dn; - int suspended; + pa_bool_t suspended; uint32_t di; pa_assert(pd); @@ -342,7 +342,7 @@ void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUS pa_context *c = userdata; pa_stream *s; uint32_t channel; - int suspended; + pa_bool_t suspended; pa_assert(pd); pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED || command == PA_COMMAND_RECORD_STREAM_SUSPENDED); @@ -543,15 +543,31 @@ static void create_stream_complete(pa_stream *s) { } } -static void automatic_buffer_attr(pa_buffer_attr *attr, pa_sample_spec *ss) { +static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_sample_spec *ss) { + pa_assert(s); pa_assert(attr); pa_assert(ss); - attr->tlength = pa_bytes_per_second(ss)/2; - attr->maxlength = (attr->tlength*3)/2; - attr->minreq = attr->tlength/50; - attr->prebuf = attr->tlength - attr->minreq; - attr->fragsize = attr->tlength/50; + if (s->context->version >= 13) + return; + + /* Version older than 0.9.10 didn't do server side buffer_attr + * selection, hence we have to fake it on the client side */ + + if (!attr->maxlength <= 0) + attr->maxlength = 4*1024*1024; /* 4MB is the maximum queue length PulseAudio <= 0.9.9 supported. */ + + if (!attr->tlength <= 0) + attr->tlength = pa_bytes_per_second(ss)*2; /* 2s of buffering */ + + if (!attr->minreq <= 0) + attr->minreq = (9*attr->tlength)/10; /* Ask for more data when there are only 200ms left in the playback buffer */ + + if (!attr->prebuf) + attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */ + + if (!attr->fragsize) + attr->fragsize = attr->tlength; /* Pass data to the app only when the buffer is filled up once */ } void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -601,7 +617,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_sample_spec ss; pa_channel_map cm; const char *dn = NULL; - int suspended; + pa_bool_t suspended; if (pa_tagstruct_get_sample_spec(t, &ss) < 0 || pa_tagstruct_get_channel_map(t, &cm) < 0 || @@ -631,7 +647,8 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_buffer_attr attr; pa_operation *o; - automatic_buffer_attr(&attr, &ss); + memset(&attr, 0, sizeof(attr)); + automatic_buffer_attr(s, &attr, &ss); /* If we need to update the buffer metrics, we wait for * the the OK for that call before we go to @@ -718,7 +735,9 @@ static int create_stream( PA_STREAM_FIX_CHANNELS| PA_STREAM_DONT_MOVE| PA_STREAM_VARIABLE_RATE| - PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID); + PA_STREAM_PEAK_DETECT| + PA_STREAM_START_MUTED| + PA_STREAM_ADJUST_LATENCY)), 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); @@ -727,6 +746,8 @@ static int create_stream( * when they are passed but actually not supported. This makes * client development easier */ + PA_CHECK_VALIDITY(s->context, direction != PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, direction != PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID); 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); @@ -742,15 +763,12 @@ static int create_stream( s->buffer_attr = *attr; s->manual_buffer_attr = TRUE; } else { - /* half a second, with minimum request of 10 ms */ - s->buffer_attr.tlength = pa_bytes_per_second(&s->sample_spec)/2; - s->buffer_attr.maxlength = (s->buffer_attr.tlength*3)/2; - s->buffer_attr.minreq = s->buffer_attr.tlength/50; - s->buffer_attr.prebuf = s->buffer_attr.tlength - s->buffer_attr.minreq; - s->buffer_attr.fragsize = s->buffer_attr.tlength/50; + memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); s->manual_buffer_attr = FALSE; } + automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec); + if (!dev) dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source; @@ -805,11 +823,16 @@ static int create_stream( if (s->context->version >= 13) { + if (s->direction == PA_STREAM_PLAYBACK) + pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED); + else + pa_tagstruct_put_boolean(t, flags & PA_STREAM_PEAK_DETECT); + pa_init_proplist(s->proplist); pa_tagstruct_put( t, - PA_TAG_BOOLEAN, flags & PA_STREAM_PEAK_DETECT, + PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY, PA_TAG_PROPLIST, s->proplist, PA_TAG_INVALID); } @@ -1023,6 +1046,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, pa_operation *o = userdata; struct timeval local, remote, now; pa_timing_info *i; + pa_bool_t playing = FALSE; pa_assert(pd); pa_assert(o); @@ -1047,7 +1071,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 || pa_tagstruct_get_usec(t, &i->source_usec) < 0 || - pa_tagstruct_get_boolean(t, &i->playing) < 0 || + pa_tagstruct_get_boolean(t, &playing) < 0 || pa_tagstruct_get_timeval(t, &local) < 0 || pa_tagstruct_get_timeval(t, &remote) < 0 || pa_tagstruct_gets64(t, &i->write_index) < 0 || @@ -1058,6 +1082,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, } else { o->stream->timing_info_valid = 1; + i->playing = (int) playing; pa_gettimeofday(&now); diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 20510fc1..ca4be593 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -155,10 +155,10 @@ static const struct command commands[] = { { "load-sample-dir-lazy", pa_cli_command_scache_load_dir, "Lazily load all files in a directory into the sample cache (args: pathname)", 2}, { "play-file", pa_cli_command_play_file, "Play a sound file (args: filename, sink|index)", 3}, { "list-autoload", pa_cli_command_autoload_list, "List autoload entries", 1}, - { "add-autoload-sink", pa_cli_command_autoload_add, "Add autoload entry for a sink (args: sink, module name, arguments)", 4}, - { "add-autoload-source", pa_cli_command_autoload_add, "Add autoload entry for a source (args: source, module name, arguments)", 4}, - { "remove-autoload-sink", pa_cli_command_autoload_remove, "Remove autoload entry for a sink (args: name)", 2}, - { "remove-autoload-source", pa_cli_command_autoload_remove, "Remove autoload entry for a source (args: name)", 2}, + { "add-autoload-sink", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4}, + { "add-autoload-source", pa_cli_command_autoload_add, NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4}, + { "remove-autoload-sink", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a sink (args: name)"*/, 2}, + { "remove-autoload-source", pa_cli_command_autoload_remove, NULL /*"Remove autoload entry for a source (args: name)"*/, 2}, { "dump", pa_cli_command_dump, "Dump daemon configuration", 1}, { "list-props", pa_cli_command_list_props, NULL, 1}, { "move-sink-input", pa_cli_command_move_sink_input, "Move sink input to another sink (args: index, sink)", 3}, @@ -367,7 +367,7 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b pa_cli_command_sink_inputs(c, t, buf, fail); pa_cli_command_source_outputs(c, t, buf, fail); pa_cli_command_scache_list(c, t, buf, fail); - pa_cli_command_autoload_list(c, t, buf, fail); +/* pa_cli_command_autoload_list(c, t, buf, fail); */ return 0; } @@ -905,6 +905,8 @@ static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *b pa_assert(buf); pa_assert(fail); + pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server."); + if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) { pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n"); return -1; @@ -923,6 +925,8 @@ static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf pa_assert(buf); pa_assert(fail); + pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server."); + if (!(name = pa_tokenizer_get(t, 1))) { pa_strbuf_puts(buf, "You need to specify a device name\n"); return -1; @@ -944,6 +948,8 @@ static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf * pa_assert(buf); pa_assert(fail); + pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server."); + pa_assert_se(s = pa_autoload_list_to_string(c)); pa_strbuf_puts(buf, s); pa_xfree(s); diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index 1bf2cfbb..a301bba0 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -204,4 +204,17 @@ static inline const char *pa_strnull(const char *x) { return x ? x : "(null)"; } +#ifdef __GNUC__ + +#define PA_WARN_REFERENCE(sym,msg) \ + __asm__(".section .gnu.warning.sym"); \ + __asm__(".asciz \"msg\""); \ + __asm__(".previous") + +#else + +#define PA_WARN_REFERENCE(sym,msg) + +#endif + #endif diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index cc5e9e13..1d9583e1 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -138,7 +138,7 @@ static void fix_current_read(pa_memblockq *bq) { break; /* Scan right */ - while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + bq->current_read->chunk.length <= bq->read_index)) + while (PA_LIKELY(bq->current_read != NULL) && PA_UNLIKELY(bq->current_read->index + (int64_t) bq->current_read->chunk.length <= bq->read_index)) bq->current_read = bq->current_read->next; /* At this point current_read will either point at or left of the @@ -158,7 +158,7 @@ static void fix_current_write(pa_memblockq *bq) { bq->current_write = bq->blocks_tail; /* Scan right */ - while (PA_UNLIKELY(bq->current_write->index + bq->current_write->chunk.length <= bq->write_index)) + while (PA_UNLIKELY(bq->current_write->index + (int64_t) bq->current_write->chunk.length <= bq->write_index)) if (bq->current_write->next) bq->current_write = bq->current_write->next; @@ -214,7 +214,7 @@ static void drop_backlog(pa_memblockq *bq) { boundary = bq->read_index - bq->maxrewind; - while (bq->blocks && (bq->blocks->index + bq->blocks->chunk.length <= boundary)) + while (bq->blocks && (bq->blocks->index + (int64_t) bq->blocks->chunk.length <= boundary)) drop_block(bq, bq->blocks); } @@ -232,10 +232,10 @@ static pa_bool_t can_push(pa_memblockq *bq, size_t l) { return TRUE; } - end = bq->blocks_tail ? bq->blocks_tail->index + bq->blocks_tail->chunk.length : bq->write_index; + end = bq->blocks_tail ? bq->blocks_tail->index + (int64_t) bq->blocks_tail->chunk.length : bq->write_index; /* Make sure that the list doesn't get too long */ - if (bq->write_index + l > end) + if (bq->write_index + (int64_t) l > end) if (bq->write_index + l - bq->read_index > bq->maxlength) return FALSE; @@ -269,7 +269,7 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) { * write to */ if (q) { - while (bq->write_index + chunk.length > q->index) + while (bq->write_index + (int64_t) chunk.length > q->index) if (q->next) q = q->next; else @@ -284,10 +284,10 @@ int pa_memblockq_push(pa_memblockq* bq, const pa_memchunk *uchunk) { while (q) { - if (bq->write_index >= q->index + q->chunk.length) + if (bq->write_index >= q->index + (int64_t) q->chunk.length) /* We found the entry where we need to place the new entry immediately after */ break; - else if (bq->write_index + chunk.length <= q->index) { + else if (bq->write_index + (int64_t) chunk.length <= q->index) { /* This entry isn't touched at all, let's skip it */ q = q->prev; } else if (bq->write_index <= q->index && @@ -407,7 +407,7 @@ finish: delta = bq->write_index - old; - if (delta >= bq->requested) { + if (delta >= (int64_t) bq->requested) { delta -= bq->requested; bq->requested = 0; } else { @@ -526,7 +526,7 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) { pa_assert(p >= bq->read_index); d = p - bq->read_index; - if (d > length) + if (d > (int64_t) length) d = length; bq->read_index += d; @@ -606,7 +606,7 @@ void pa_memblockq_seek(pa_memblockq *bq, int64_t offset, pa_seek_mode_t seek) { delta = bq->write_index - old; - if (delta >= bq->requested) { + if (delta >= (int64_t) bq->requested) { delta -= bq->requested; bq->requested = 0; } else if (delta >= 0) { @@ -633,7 +633,7 @@ void pa_memblockq_flush(pa_memblockq *bq) { delta = bq->write_index - old; - if (delta >= bq->requested) { + if (delta >= (int64_t) bq->requested) { delta -= bq->requested; bq->requested = 0; } else if (delta >= 0) { diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index ae140ff4..8e5bd2d1 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -109,8 +109,8 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { m->userdata = NULL; m->core = c; m->n_used = -1; - m->auto_unload = 0; - m->unload_requested = 0; + m->auto_unload = FALSE; + m->unload_requested = FALSE; if (m->init(m) < 0) { pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : ""); @@ -281,7 +281,7 @@ static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) { void pa_module_unload_request(pa_module *m) { pa_assert(m); - m->unload_requested = 1; + m->unload_requested = TRUE; if (!m->core->module_defer_unload_event) m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core); diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h index 25f122d1..68c7238d 100644 --- a/src/pulsecore/module.h +++ b/src/pulsecore/module.h @@ -45,10 +45,10 @@ struct pa_module { void *userdata; int n_used; - int auto_unload; + pa_bool_t auto_unload; time_t last_used_time; - int unload_requested; + pa_bool_t unload_requested; }; pa_module* pa_module_load(pa_core *c, const char *name, const char*argument); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 0ac3ec1f..59d1612c 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -71,6 +71,8 @@ #define MAX_CONNECTIONS 64 #define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */ +#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */ +#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC typedef struct connection connection; struct pa_protocol_native; @@ -469,9 +471,10 @@ static record_stream* record_stream_new( pa_channel_map *map, const char *name, uint32_t *maxlength, - uint32_t fragment_size, + uint32_t *fragsize, pa_source_output_flags_t flags, - pa_proplist *p) { + pa_proplist *p, + pa_bool_t adjust_latency) { record_stream *s; pa_source_output *source_output; @@ -482,7 +485,6 @@ static record_stream* record_stream_new( pa_assert(ss); pa_assert(name); pa_assert(maxlength); - pa_assert(*maxlength > 0); pa_assert(p); pa_source_output_new_data_init(&data); @@ -508,6 +510,7 @@ static record_stream* record_stream_new( s->parent.process_msg = record_stream_process_msg; s->connection = c; s->source_output = source_output; + s->source_output->push = source_output_push_cb; s->source_output->kill = source_output_kill_cb; s->source_output->get_latency = source_output_get_latency_cb; @@ -515,11 +518,36 @@ static record_stream* record_stream_new( s->source_output->suspend = source_output_suspend_cb; s->source_output->userdata = s; + if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) + *maxlength = MAX_MEMBLOCKQ_LENGTH; + if (*fragsize <= 0) + *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*1000, &source_output->sample_spec); + + if (adjust_latency) { + pa_usec_t fragsize_usec, source_latency; + + /* So, the user asked us to adjust the latency according to + * the what the source can provide. Half the latency will be + * spent on the hw buffer, half of it in the async buffer + * queue we maintain for each client. */ + + fragsize_usec = pa_bytes_to_usec(*fragsize, &source_output->sample_spec); + + source_latency = pa_source_output_set_requested_latency(source_output, fragsize_usec/2); + + if (fragsize_usec >= source_latency*2) + fragsize_usec -= source_latency; + else + fragsize_usec = source_latency; + + *fragsize = pa_usec_to_bytes(fragsize_usec, &source_output->sample_spec); + } + s->memblockq = pa_memblockq_new( 0, *maxlength, 0, - base = pa_frame_size(&s->source_output->sample_spec), + base = pa_frame_size(&source_output->sample_spec), 1, 0, 0, @@ -527,13 +555,15 @@ static record_stream* record_stream_new( *maxlength = pa_memblockq_get_maxlength(s->memblockq); - s->fragment_size = (fragment_size/base)*base; + s->fragment_size = (*fragsize/base)*base; if (s->fragment_size <= 0) s->fragment_size = base; if (s->fragment_size > *maxlength) s->fragment_size = *maxlength; + *fragsize = s->fragment_size; + *ss = s->source_output->sample_spec; *map = s->source_output->channel_map; @@ -658,10 +688,12 @@ static playback_stream* playback_stream_new( uint32_t *prebuf, uint32_t *minreq, pa_cvolume *volume, + pa_bool_t muted, uint32_t syncid, uint32_t *missing, pa_sink_input_flags_t flags, - pa_proplist *p) { + pa_proplist *p, + pa_bool_t adjust_latency) { playback_stream *s, *ssync; pa_sink_input *sink_input; @@ -674,6 +706,11 @@ static playback_stream* playback_stream_new( pa_assert(ss); pa_assert(name); pa_assert(maxlength); + pa_assert(tlength); + pa_assert(prebuf); + pa_assert(minreq); + pa_assert(volume); + pa_assert(missing); pa_assert(p); /* Find syncid group */ @@ -706,6 +743,7 @@ static playback_stream* playback_stream_new( pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_channel_map(&data, map); pa_sink_input_new_data_set_volume(&data, volume); + pa_sink_input_new_data_set_muted(&data, muted); data.sync_base = ssync ? ssync->sink_input : NULL; sink_input = pa_sink_input_new(c->protocol->core, &data, flags); @@ -732,13 +770,49 @@ static playback_stream* playback_stream_new( start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0; - silence = pa_silence_memblock_new(c->protocol->core->mempool, &s->sink_input->sample_spec, 0); + if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) + *maxlength = MAX_MEMBLOCKQ_LENGTH; + if (*tlength <= 0) + *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*1000, &sink_input->sample_spec); + if (*minreq <= 0) + *minreq = (*tlength*9)/10; + if (*prebuf <= 0) + *prebuf = *tlength; + + if (adjust_latency) { + pa_usec_t tlength_usec, minreq_usec, sink_latency; + + /* So, the user asked us to adjust the latency according to + * the what the sink can provide. Half the latency will be + * spent on the hw buffer, half of it in the async buffer + * queue we maintain for each client. */ + + tlength_usec = pa_bytes_to_usec(*tlength, &sink_input->sample_spec); + minreq_usec = pa_bytes_to_usec(*minreq, &sink_input->sample_spec); + + sink_latency = pa_sink_input_set_requested_latency(sink_input, tlength_usec/2); + + if (tlength_usec >= sink_latency*2) + tlength_usec -= sink_latency; + else + tlength_usec = sink_latency; + + if (minreq_usec >= sink_latency*2) + minreq_usec -= sink_latency; + else + minreq_usec = sink_latency; + + *tlength = pa_usec_to_bytes(tlength_usec, &sink_input->sample_spec); + *minreq = pa_usec_to_bytes(minreq_usec, &sink_input->sample_spec); + } + + silence = pa_silence_memblock_new(c->protocol->core->mempool, &sink_input->sample_spec, 0); s->memblockq = pa_memblockq_new( start_index, *maxlength, *tlength, - pa_frame_size(&s->sink_input->sample_spec), + pa_frame_size(&sink_input->sample_spec), *prebuf, *minreq, 0, @@ -762,7 +836,6 @@ static playback_stream* playback_stream_new( pa_idxset_put(c->output_streams, s, &s->index); pa_sink_input_put(s->sink_input); - return s; } @@ -1230,8 +1303,18 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC pa_tagstruct *reply; pa_sink *sink = NULL; pa_cvolume volume; - int corked; - int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0; + pa_bool_t + corked = FALSE, + no_remap = FALSE, + no_remix = FALSE, + fix_format = FALSE, + fix_rate = FALSE, + fix_channels = FALSE, + no_move = FALSE, + variable_rate = FALSE, + muted = FALSE, + adjust_latency = FALSE; + pa_sink_input_flags_t flags = 0; pa_proplist *p; @@ -1264,8 +1347,6 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID); p = pa_proplist_new(); @@ -1291,7 +1372,9 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC if (c->version >= 13) { - if (pa_tagstruct_get_proplist(t, p) < 0) { + if (pa_tagstruct_get_boolean(t, &muted) < 0 || + pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || + pa_tagstruct_get_proplist(t, p) < 0) { protocol_error(c); pa_proplist_free(p); return; @@ -1331,7 +1414,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0); - s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, syncid, &missing, flags, p); + s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1438,8 +1521,16 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ pa_channel_map map; pa_tagstruct *reply; pa_source *source = NULL; - int corked; - int no_remap = 0, no_remix = 0, fix_format = 0, fix_rate = 0, fix_channels = 0, no_move = 0, variable_rate = 0; + pa_bool_t + corked = FALSE, + no_remap = FALSE, + no_remix = FALSE, + fix_format = FALSE, + fix_rate = FALSE, + fix_channels = FALSE, + no_move = FALSE, + variable_rate = FALSE, + adjust_latency = FALSE; pa_source_output_flags_t flags = 0; pa_proplist *p; @@ -1463,8 +1554,6 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, source_index != PA_INVALID_INDEX || !source_name || (*source_name && pa_utf8_valid(source_name)), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID); p = pa_proplist_new(); @@ -1490,7 +1579,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ if (c->version >= 13) { - if (pa_tagstruct_get_proplist(t, p) < 0) { + if (pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || + pa_tagstruct_get_proplist(t, p) < 0) { protocol_error(c); pa_proplist_free(p); return; @@ -1530,7 +1620,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); - s = record_stream_new(c, source, &ss, &map, name, &maxlength, fragment_size, flags, p); + s = record_stream_new(c, source, &ss, &map, name, &maxlength, &fragment_size, flags, p, adjust_latency); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1544,7 +1634,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ /* Since 0.9 we support sending the buffer metrics back to the client */ pa_tagstruct_putu32(reply, (uint32_t) maxlength); - pa_tagstruct_putu32(reply, (uint32_t) s->fragment_size); + pa_tagstruct_putu32(reply, (uint32_t) fragment_size); } if (c->version >= 12) { @@ -1873,7 +1963,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN reply = reply_new(tag); pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0); pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source)); - pa_tagstruct_put_boolean(reply, 0); + pa_tagstruct_put_boolean(reply, FALSE); pa_tagstruct_put_timeval(reply, &tv); pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now)); pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq)); @@ -2511,7 +2601,7 @@ static void command_set_mute( connection *c = CONNECTION(userdata); uint32_t idx; - int mute; + pa_bool_t mute; pa_sink *sink = NULL; pa_source *source = NULL; pa_sink_input *si = NULL; @@ -2574,7 +2664,7 @@ static void command_set_mute( static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { connection *c = CONNECTION(userdata); uint32_t idx; - int b; + pa_bool_t b; playback_stream *s; connection_assert_ref(c); @@ -2641,7 +2731,7 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN connection *c = CONNECTION(userdata); uint32_t idx; record_stream *s; - int b; + pa_bool_t b; connection_assert_ref(c); pa_assert(t); @@ -2719,8 +2809,14 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u return; } - CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID); + if (maxlength <= 0 || maxlength > MAX_MEMBLOCKQ_LENGTH) + maxlength = MAX_MEMBLOCKQ_LENGTH; + if (tlength <= 0) + tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*1000, &s->sink_input->sample_spec); + if (minreq <= 0) + minreq = (tlength*9)/10; + if (prebuf <= 0) + prebuf = tlength; pa_memblockq_set_maxlength(s->memblockq, maxlength); pa_memblockq_set_tlength(s->memblockq, tlength); @@ -2751,8 +2847,10 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u return; } - CHECK_VALIDITY(c->pstream, maxlength > 0, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, maxlength <= MAX_MEMBLOCKQ_LENGTH, tag, PA_ERR_INVALID); + if (maxlength <= 0 || maxlength > MAX_MEMBLOCKQ_LENGTH) + maxlength = MAX_MEMBLOCKQ_LENGTH; + if (fragsize <= 0) + fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*1000, &s->source_output->sample_spec); pa_memblockq_set_maxlength(s->memblockq, maxlength); @@ -3336,7 +3434,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa connection *c = CONNECTION(userdata); uint32_t idx = PA_INVALID_INDEX; const char *name = NULL; - int b; + pa_bool_t b; connection_assert_ref(c); pa_assert(t); diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index 7c764e3a..59341f04 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -282,7 +282,9 @@ int pa_shm_attach_ro(pa_shm *m, unsigned id) { goto fail; } - if (st.st_size <= 0 || st.st_size > MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker)) || PA_ALIGN(st.st_size) != st.st_size) { + if (st.st_size <= 0 || + st.st_size > (off_t) (MAX_SHM_SIZE+PA_ALIGN(sizeof(struct shm_marker))) || + PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) { pa_log("Invalid shared memory segment size"); goto fail; } diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index e15aa7bb..de699454 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -665,11 +665,18 @@ void pa_sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes /* in the sin i->set_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes); } -void pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { +pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); - pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); + if (usec < i->sink->min_latency) + usec = i->sink->min_latency; + + if (PA_SINK_INPUT_LINKED(i->state)) + pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); + else + i->thread_info.requested_sink_latency = usec; + + return usec; } void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index c74b8912..62464ca2 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -226,7 +226,7 @@ void pa_sink_input_unlink(pa_sink_input* i); void pa_sink_input_set_name(pa_sink_input *i, const char *name); -void pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec); +pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec); /* Request that the specified number of bytes already written out to the hw device is rewritten, if possible. If this function is used you diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index cded6ba7..39ce83f1 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ #define MAX_MIX_CHANNELS 32 #define MIX_BUFFER_LENGTH (PA_PAGE_SIZE) +#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC) static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject); @@ -185,6 +187,8 @@ pa_sink* pa_sink_new( s->rtpoll = NULL; s->silence = pa_silence_memblock_new(core->mempool, &s->sample_spec, 0); + s->min_latency = DEFAULT_MIN_LATENCY; + s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); s->thread_info.soft_volume = s->volume; s->thread_info.soft_muted = s->muted; diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 3f169952..23f02c40 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -91,6 +91,8 @@ struct pa_sink { pa_memblock *silence; + pa_usec_t min_latency; /* we won't go below this latency setting */ + int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */ int (*set_volume)(pa_sink *s); /* dito */ int (*get_volume)(pa_sink *s); /* dito */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 45a8d74e..d6c2a2b2 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -360,13 +360,20 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_memblock_unref(rchunk.memblock); } -void pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { +pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); - pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); -} + if (usec < o->source->min_latency) + usec = o->source->min_latency; + if (PA_SOURCE_OUTPUT_LINKED(o->state)) + pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); + else + o->thread_info.requested_source_latency = usec; + + return usec; +} void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { pa_source_output_assert_ref(o); diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index dc95217b..0a2286b9 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -174,7 +174,7 @@ void pa_source_output_unlink(pa_source_output*o); void pa_source_output_set_name(pa_source_output *i, const char *name); -void pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec); +pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec); /* Callable by everyone */ diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index cc1c531d..b150d8ac 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -41,6 +42,8 @@ #include "source.h" +#define DEFAULT_MIN_LATENCY (4*PA_USEC_PER_MSEC) + static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject); static void source_free(pa_object *o); @@ -162,6 +165,8 @@ pa_source* pa_source_new( s->muted = data->muted; s->refresh_volume = s->refresh_muted = FALSE; + s->min_latency = DEFAULT_MIN_LATENCY; + s->get_latency = NULL; s->set_volume = NULL; s->get_volume = NULL; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index c880d3c5..3ec8320d 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -91,6 +91,8 @@ struct pa_source { pa_asyncmsgq *asyncmsgq; pa_rtpoll *rtpoll; + pa_usec_t min_latency; /* we won't go below this latency setting */ + int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */ int (*set_volume)(pa_source *s); /* dito */ int (*get_volume)(pa_source *s); /* dito */ diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c index 0017c388..92bace27 100644 --- a/src/pulsecore/tagstruct.c +++ b/src/pulsecore/tagstruct.c @@ -163,7 +163,7 @@ void pa_tagstruct_put_arbitrary(pa_tagstruct *t, const void *p, size_t length) { t->length += 5+length; } -void pa_tagstruct_put_boolean(pa_tagstruct*t, int b) { +void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b) { pa_assert(t); extend(t, 1); @@ -407,7 +407,7 @@ const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l) { return t->data; } -int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) { +int pa_tagstruct_get_boolean(pa_tagstruct*t, pa_bool_t *b) { pa_assert(t); pa_assert(b); @@ -415,9 +415,9 @@ int pa_tagstruct_get_boolean(pa_tagstruct*t, int *b) { return -1; if (t->data[t->rindex] == PA_TAG_BOOLEAN_TRUE) - *b = 1; + *b = TRUE; else if (t->data[t->rindex] == PA_TAG_BOOLEAN_FALSE) - *b = 0; + *b = FALSE; else return -1; @@ -725,7 +725,7 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) { case PA_TAG_BOOLEAN_TRUE: case PA_TAG_BOOLEAN_FALSE: - ret = pa_tagstruct_get_boolean(t, va_arg(va, int*)); + ret = pa_tagstruct_get_boolean(t, va_arg(va, pa_bool_t*)); break; case PA_TAG_TIMEVAL: diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h index 3b2ce7b9..8846b301 100644 --- a/src/pulsecore/tagstruct.h +++ b/src/pulsecore/tagstruct.h @@ -72,7 +72,7 @@ void pa_tagstruct_putu64(pa_tagstruct*t, uint64_t i); void pa_tagstruct_puts64(pa_tagstruct*t, int64_t i); void pa_tagstruct_put_sample_spec(pa_tagstruct *t, const pa_sample_spec *ss); void pa_tagstruct_put_arbitrary(pa_tagstruct*t, const void *p, size_t length); -void pa_tagstruct_put_boolean(pa_tagstruct*t, int b); +void pa_tagstruct_put_boolean(pa_tagstruct*t, pa_bool_t b); void pa_tagstruct_put_timeval(pa_tagstruct*t, const struct timeval *tv); void pa_tagstruct_put_usec(pa_tagstruct*t, pa_usec_t u); void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map); @@ -88,7 +88,7 @@ int pa_tagstruct_getu64(pa_tagstruct*t, uint64_t *i); int pa_tagstruct_gets64(pa_tagstruct*t, int64_t *i); int pa_tagstruct_get_sample_spec(pa_tagstruct *t, pa_sample_spec *ss); int pa_tagstruct_get_arbitrary(pa_tagstruct *t, const void **p, size_t length); -int pa_tagstruct_get_boolean(pa_tagstruct *t, int *b); +int pa_tagstruct_get_boolean(pa_tagstruct *t, pa_bool_t *b); int pa_tagstruct_get_timeval(pa_tagstruct*t, struct timeval *tv); int pa_tagstruct_get_usec(pa_tagstruct*t, pa_usec_t *u); int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map); -- cgit From 122861f75eb0bda396a2a6e13fa4074a6d50d353 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 4 Apr 2008 16:04:29 +0000 Subject: mark libpulse-browse as obsolete git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2216 fefdeb5f-60dc-0310-8127-8f9354f1896f --- doxygen/doxygen.conf.in | 2 +- src/pulse/browser.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in index 81923a9f..8ccf667a 100644 --- a/doxygen/doxygen.conf.in +++ b/doxygen/doxygen.conf.in @@ -417,7 +417,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/browser.h +INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp diff --git a/src/pulse/browser.c b/src/pulse/browser.c index 55e0b2cd..5e4aa87b 100644 --- a/src/pulse/browser.c +++ b/src/pulse/browser.c @@ -313,10 +313,15 @@ static void client_callback(AvahiClient *s, AvahiClientState state, void *userda static void browser_free(pa_browser *b); + +PA_WARN_REFERENCE(pa_browser_new, "libpulse-browse is being phased out."); + pa_browser *pa_browser_new(pa_mainloop_api *mainloop) { return pa_browser_new_full(mainloop, PA_BROWSE_FOR_SERVERS|PA_BROWSE_FOR_SINKS|PA_BROWSE_FOR_SOURCES, NULL); } +PA_WARN_REFERENCE(pa_browser_new_full, "libpulse-browse is being phased out."); + pa_browser *pa_browser_new_full(pa_mainloop_api *mainloop, pa_browse_flags_t flags, const char **error_string) { pa_browser *b; int error; @@ -420,6 +425,8 @@ static void browser_free(pa_browser *b) { pa_xfree(b); } +PA_WARN_REFERENCE(pa_browser_ref, "libpulse-browse is being phased out."); + pa_browser *pa_browser_ref(pa_browser *b) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); @@ -428,6 +435,8 @@ pa_browser *pa_browser_ref(pa_browser *b) { return b; } +PA_WARN_REFERENCE(pa_browser_unref, "libpulse-browse is being phased out."); + void pa_browser_unref(pa_browser *b) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); @@ -436,6 +445,8 @@ void pa_browser_unref(pa_browser *b) { browser_free(b); } +PA_WARN_REFERENCE(pa_browser_set_callback, "libpulse-browse is being phased out."); + void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); @@ -444,6 +455,8 @@ void pa_browser_set_callback(pa_browser *b, pa_browse_cb_t cb, void *userdata) { b->userdata = userdata; } +PA_WARN_REFERENCE(pa_browser_set_error_callback, "libpulse-browse is being phased out."); + void pa_browser_set_error_callback(pa_browser *b, pa_browser_error_cb_t cb, void *userdata) { pa_assert(b); pa_assert(PA_REFCNT_VALUE(b) >= 1); -- cgit From 064aa1243c202dd99179ebe34f7ed92ef6f40d12 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 4 Apr 2008 16:05:15 +0000 Subject: drop support for periodic timers, cleanup code a bit git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2217 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/rtpoll.c | 52 ++++++-------------------------------------------- src/pulsecore/rtpoll.h | 1 - 2 files changed, 6 insertions(+), 47 deletions(-) diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index f7773be3..74035081 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -63,7 +63,6 @@ struct pa_rtpoll { pa_bool_t timer_enabled; struct timeval next_elapse; - pa_usec_t period; pa_bool_t scan_for_dead; pa_bool_t running, installed, rebuild_needed, quit; @@ -139,7 +138,6 @@ pa_rtpoll *pa_rtpoll_new(void) { p->pollfd2 = pa_xnew(struct pollfd, p->n_pollfd_alloc); p->n_pollfd_used = 0; - p->period = 0; memset(&p->next_elapse, 0, sizeof(p->next_elapse)); p->timer_enabled = FALSE; @@ -368,15 +366,13 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) { if (p->rebuild_needed) rtpoll_rebuild(p); + memset(&timeout, 0, sizeof(timeout)); + /* Calculate timeout */ - if (!wait || p->quit) { - timeout.tv_sec = 0; - timeout.tv_usec = 0; - } else if (p->timer_enabled) { + if (wait && !p->quit && p->timer_enabled) { struct timeval now; pa_rtclock_get(&now); - memset(&timeout, 0, sizeof(timeout)); if (pa_timeval_cmp(&p->next_elapse, &now) > 0) pa_timeval_add(&timeout, pa_timeval_diff(&p->next_elapse, &now)); } @@ -391,14 +387,14 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) { struct timespec ts; ts.tv_sec = timeout.tv_sec; ts.tv_nsec = timeout.tv_usec * 1000; - r = ppoll(p->pollfd, p->n_pollfd_used, p->timer_enabled ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked); + r = ppoll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? &ts : NULL, p->rtsig < 0 ? NULL : &p->sigset_unblocked); } #ifdef __linux__ else #endif #endif - r = poll(p->pollfd, p->n_pollfd_used, p->timer_enabled ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1); + r = poll(p->pollfd, p->n_pollfd_used, (!wait || p->quit || p->timer_enabled) ? (timeout.tv_sec*1000) + (timeout.tv_usec / 1000) : -1); if (r < 0) { if (errno == EAGAIN || errno == EINTR) @@ -409,21 +405,6 @@ int pa_rtpoll_run(pa_rtpoll *p, pa_bool_t wait) { reset_all_revents(p); } - if (p->timer_enabled) { - if (p->period > 0) { - struct timeval now; - pa_rtclock_get(&now); - - pa_timeval_add(&p->next_elapse, p->period); - - /* Guarantee that the next timeout will happen in the future */ - if (pa_timeval_cmp(&p->next_elapse, &now) < 0) - pa_timeval_add(&p->next_elapse, (pa_timeval_diff(&now, &p->next_elapse) / p->period + 1) * p->period); - - } else - p->timer_enabled = FALSE; - } - /* Let's tell everyone that we left the sleep */ for (i = p->items; i && i->priority < PA_RTPOLL_NEVER; i = i->next) { @@ -489,15 +470,8 @@ static void update_timer(pa_rtpoll *p) { /* Make sure that 0,0 is not understood as * "disarming" */ - if (its.it_value.tv_sec == 0) + if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) its.it_value.tv_nsec = 1; - - if (p->period > 0) { - struct timeval tv; - pa_timeval_store(&tv, p->period); - its.it_interval.tv_sec = tv.tv_sec; - its.it_interval.tv_nsec = tv.tv_usec*1000; - } } pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); @@ -515,18 +489,6 @@ void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts) { pa_assert(ts); p->next_elapse = *ts; - p->period = 0; - p->timer_enabled = TRUE; - - update_timer(p); -} - -void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec) { - pa_assert(p); - - p->period = usec; - pa_rtclock_get(&p->next_elapse); - pa_timeval_add(&p->next_elapse, usec); p->timer_enabled = TRUE; update_timer(p); @@ -535,7 +497,6 @@ void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec) { void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) { pa_assert(p); - p->period = 0; pa_rtclock_get(&p->next_elapse); pa_timeval_add(&p->next_elapse, usec); p->timer_enabled = TRUE; @@ -546,7 +507,6 @@ void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec) { void pa_rtpoll_set_timer_disabled(pa_rtpoll *p) { pa_assert(p); - p->period = 0; memset(&p->next_elapse, 0, sizeof(p->next_elapse)); p->timer_enabled = FALSE; diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h index 02f5c7c2..f7f96e67 100644 --- a/src/pulsecore/rtpoll.h +++ b/src/pulsecore/rtpoll.h @@ -75,7 +75,6 @@ void pa_rtpoll_install(pa_rtpoll *p); int pa_rtpoll_run(pa_rtpoll *f, pa_bool_t wait); void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts); -void pa_rtpoll_set_timer_periodic(pa_rtpoll *p, pa_usec_t usec); void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec); void pa_rtpoll_set_timer_disabled(pa_rtpoll *p); -- cgit From b9c10f2c56fc34b5ff99b0a9b3959b4d37586a39 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 4 Apr 2008 16:06:47 +0000 Subject: propery calculate min_avail in frames instead of bytes. don't use device_id= parameter in alsa modules if parameter wasn't specified git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2218 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.c | 2 +- src/modules/alsa-util.h | 2 +- src/modules/module-alsa-sink.c | 67 +++++++++++++++++++++++++++------------- src/modules/module-alsa-source.c | 2 +- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index d899eaec..38ea8633 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -426,7 +426,7 @@ finish: return ret; } -int pa_alsa_set_sw_params(snd_pcm_t *pcm, size_t avail_min) { +int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) { snd_pcm_sw_params_t *swparams; int err; diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 62c1d434..dee955fd 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -48,7 +48,7 @@ int pa_alsa_set_hw_params( pa_bool_t *use_tsched, pa_bool_t require_exact_channel_number); -int pa_alsa_set_sw_params(snd_pcm_t *pcm, size_t avail_min); +int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min); int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev); snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback); diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 1dccf670..cd3ebcc2 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -74,7 +74,7 @@ PA_MODULE_USAGE( #define DEFAULT_DEVICE "default" #define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) -#define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC) +#define DEFAULT_TSCHED_WATERMARK_USEC (100*PA_USEC_PER_MSEC) struct userdata { pa_core *core; @@ -160,8 +160,10 @@ static int mmap_write(struct userdata *u) { if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) continue; - if (err == -EAGAIN) + if (err == -EAGAIN) { + pa_log_debug("EAGAIN"); return work_done; + } pa_log("snd_pcm_avail_update: %s", snd_strerror(err)); return -1; @@ -187,8 +189,10 @@ static int mmap_write(struct userdata *u) { if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) continue; - if (err == -EAGAIN) + if (err == -EAGAIN) { + pa_log_debug("EAGAIN"); return work_done; + } pa_log("Failed to write data to DSP: %s", snd_strerror(err)); return -1; @@ -226,8 +230,10 @@ static int mmap_write(struct userdata *u) { if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) continue; - if (err == -EAGAIN) + if (err == -EAGAIN) { + pa_log_debug("EAGAIN"); return work_done; + } pa_log("Failed to write data to DSP: %s", snd_strerror(err)); return -1; @@ -300,8 +306,10 @@ static int unix_write(struct userdata *u) { if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) continue; - if (frames == -EAGAIN) + if (frames == -EAGAIN) { + pa_log_debug("EAGAIN"); return work_done; + } pa_log("Failed to write data to DSP: %s", snd_strerror(frames)); return -1; @@ -468,7 +476,7 @@ static void update_hwbuf_unused_frames(struct userdata *u) { } static int update_sw_params(struct userdata *u) { - size_t avail_min; + snd_pcm_uframes_t avail_min; int err; pa_assert(u); @@ -478,7 +486,7 @@ static int update_sw_params(struct userdata *u) { usec = hw_sleep_time(u); - avail_min = pa_usec_to_bytes(usec, &u->sink->sample_spec); + avail_min = pa_usec_to_bytes(usec, &u->sink->sample_spec) / u->frame_size; if (avail_min <= 0) avail_min = 1; @@ -486,6 +494,10 @@ static int update_sw_params(struct userdata *u) { } else avail_min = 1; + avail_min = (snd_pcm_uframes_t) -1; + + pa_log("setting avail_min=%lu", (unsigned long) avail_min); + if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { pa_log("Failed to set software parameters: %s", snd_strerror(err)); return err; @@ -493,6 +505,8 @@ static int update_sw_params(struct userdata *u) { update_hwbuf_unused_frames(u); + pa_log("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames); + return 0; } @@ -810,19 +824,26 @@ static void thread_func(void *userdata) { if (frames > limit) frames = limit; - pa_log_debug("Limited to %lu bytes.", (unsigned long) frames * u->frame_size); + frames = 0; - if ((frames = snd_pcm_rewind(u->pcm_handle, frames)) < 0) { - pa_log("snd_pcm_rewind() failed: %s", snd_strerror(frames)); - goto fail; - } + if (frames > 0) { + + pa_log_debug("Limited to %lu bytes.", (unsigned long) frames * u->frame_size); + + if ((frames = snd_pcm_rewind(u->pcm_handle, frames)) < 0) { + pa_log("snd_pcm_rewind() failed: %s", snd_strerror(frames)); + goto fail; + } - if ((u->sink->thread_info.rewind_nbytes = frames * u->frame_size) <= 0) - pa_log_info("Tried rewind, but was apparently not possible."); - else { - u->frame_index -= frames; - pa_log_debug("Rewound %lu bytes.", (unsigned long) u->sink->thread_info.rewind_nbytes); - pa_sink_process_rewind(u->sink); + if ((u->sink->thread_info.rewind_nbytes = frames * u->frame_size) <= 0) + pa_log_info("Tried rewind, but was apparently not possible."); + else { + u->frame_index -= frames; + pa_log_debug("Rewound %lu bytes.", (unsigned long) u->sink->thread_info.rewind_nbytes); + pa_sink_process_rewind(u->sink); + } + } else { + pa_log_debug("Mhmm, actually there is nothing to rewind."); } } @@ -851,7 +872,7 @@ static void thread_func(void *userdata) { } if (u->use_tsched) { - pa_usec_t usec, cusec; + pa_usec_t usec, cusec, sleep_usec; /* OK, the playback buffer is now full, let's * calculate when to wake up next */ @@ -866,8 +887,12 @@ static void thread_func(void *userdata) { pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); + sleep_usec = PA_MIN(usec, cusec); + + pa_log_debug("Waking up in %0.2fms (smaller value).", (double) sleep_usec / PA_USEC_PER_MSEC); + /* We don't trust the conversion, so we wake up whatever comes first */ - pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); + pa_rtpoll_set_timer_relative(u->rtpoll, sleep_usec); } } else if (u->use_tsched) { @@ -1107,7 +1132,7 @@ int pa__init(pa_module*m) { if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0) found = TRUE; - else { + else if (dev_id) { char *md = pa_sprintf_malloc("hw:%s", dev_id); if (strcmp(u->device_name, md)) diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index af20f814..901e3427 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -979,7 +979,7 @@ int pa__init(pa_module*m) { if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0) found = TRUE; - else { + else if (dev_id) { char *md = pa_sprintf_malloc("hw:%s", dev_id); if (strcmp(u->device_name, md)) -- cgit From 98b0152d7c9fe219448edfd61777b8f7357da25c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 7 Apr 2008 16:46:13 +0000 Subject: add utility functions to dump alsa PCM state git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2219 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/modules/alsa-util.h | 3 +++ 2 files changed, 46 insertions(+) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 38ea8633..c8f594ac 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -894,3 +894,46 @@ void pa_alsa_0dB_capture(snd_mixer_elem_t *elem) { snd_mixer_selem_set_capture_volume_all(elem, v); } + +void pa_alsa_dump(snd_pcm_t *pcm) { + int err; + snd_output_t *out; + + pa_assert(pcm); + + pa_assert_se(snd_output_buffer_open(&out) == 0); + + if ((err = snd_pcm_dump(pcm, out)) < 0) + pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err)); + else { + char *s = NULL; + snd_output_buffer_string(out, &s); + pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s)); + } + + pa_assert_se(snd_output_close(out) == 0); +} + +void pa_alsa_dump_status(snd_pcm_t *pcm) { + int err; + snd_output_t *out; + snd_pcm_status_t *status; + + pa_assert(pcm); + + snd_pcm_status_alloca(&status); + + pa_assert_se(snd_output_buffer_open(&out) == 0); + + pa_assert_se(snd_pcm_status(pcm, status) == 0); + + if ((err = snd_pcm_status_dump(status, out)) < 0) + pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err)); + else { + char *s = NULL; + snd_output_buffer_string(out, &s); + pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s)); + } + + pa_assert_se(snd_output_close(out) == 0); +} diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index dee955fd..0e536f1b 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -82,4 +82,7 @@ int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel void pa_alsa_0dB_playback(snd_mixer_elem_t *elem); void pa_alsa_0dB_capture(snd_mixer_elem_t *elem); +void pa_alsa_dump(snd_pcm_t *pcm); +void pa_alsa_dump_status(snd_pcm_t *pcm); + #endif -- cgit From c84a64cf322844b7c4c78dbcc32b1fa1a461af1b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 7 Apr 2008 16:47:27 +0000 Subject: fix bug where we silently dropped data that didn't fit into one mempool tile git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2220 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink-input.c | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index de699454..989e2ae5 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -517,29 +517,38 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_assert(tchunk.length > 0); pa_assert(tchunk.memblock); - if (tchunk.length > block_size_max_sink_input) - tchunk.length = block_size_max_sink_input; + while (tchunk.length > 0) { + pa_memchunk wchunk; - /* It might be necessary to adjust the volume here */ - if (do_volume_adj_here && !volume_is_norm) { - pa_memchunk_make_writable(&tchunk, 0); + wchunk = tchunk; - if (i->thread_info.muted) - pa_silence_memchunk(&tchunk, &i->thread_info.sample_spec); - else - pa_volume_memchunk(&tchunk, &i->thread_info.sample_spec, &i->thread_info.volume); - } + if (wchunk.length > block_size_max_sink_input) + wchunk.length = block_size_max_sink_input; - if (!i->thread_info.resampler) - pa_memblockq_push_align(i->thread_info.render_memblockq, &tchunk); - else { - pa_memchunk rchunk; - pa_resampler_run(i->thread_info.resampler, &tchunk, &rchunk); + /* It might be necessary to adjust the volume here */ + if (do_volume_adj_here && !volume_is_norm) { + pa_memchunk_make_writable(&wchunk, 0); - if (rchunk.memblock) { - pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk); - pa_memblock_unref(rchunk.memblock); + if (i->thread_info.muted) + pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec); + else + pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume); } + + if (!i->thread_info.resampler) + pa_memblockq_push_align(i->thread_info.render_memblockq, &wchunk); + else { + pa_memchunk rchunk; + pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk); + + if (rchunk.memblock) { + pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk); + pa_memblock_unref(rchunk.memblock); + } + } + + tchunk.index += wchunk.length; + tchunk.length -= wchunk.length; } pa_memblock_unref(tchunk.memblock); -- cgit From b3b8a63c507a9f3dec1414510348c92d168813f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 7 Apr 2008 17:19:51 +0000 Subject: call snd_pcm_hwsync() expclicitly before we access any of the status fields, since this seems to be necessary. try to find the right mixer device via the card index git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2221 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 125 ++++++++++++++++++++++----------------- src/modules/module-alsa-source.c | 110 ++++++++++++++++++++++++---------- 2 files changed, 150 insertions(+), 85 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index cd3ebcc2..70a94ee3 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -74,7 +74,7 @@ PA_MODULE_USAGE( #define DEFAULT_DEVICE "default" #define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) -#define DEFAULT_TSCHED_WATERMARK_USEC (100*PA_USEC_PER_MSEC) +#define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC) struct userdata { pa_core *core; @@ -145,6 +145,8 @@ static int mmap_write(struct userdata *u) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset, frames; + snd_pcm_hwsync(u->pcm_handle); + /* First we determine how many samples are missing to fill the * buffer up to 100% */ @@ -152,20 +154,20 @@ static int mmap_write(struct userdata *u) { pa_log_debug("snd_pcm_avail_update: %s", snd_strerror(n)); - if (n == -EPIPE) { - pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); - u->first = TRUE; - } - - if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) - continue; - if (err == -EAGAIN) { pa_log_debug("EAGAIN"); return work_done; } - pa_log("snd_pcm_avail_update: %s", snd_strerror(err)); + if (n == -EPIPE) + pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); + + if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) { + u->first = TRUE; + continue; + } + + pa_log("snd_pcm_recover: %s", snd_strerror(err)); return -1; } @@ -181,19 +183,19 @@ static int mmap_write(struct userdata *u) { pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err)); - if (err == -EPIPE) { - pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); - u->first = TRUE; - } - - if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) - continue; - if (err == -EAGAIN) { pa_log_debug("EAGAIN"); return work_done; } + if (err == -EPIPE) + pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); + + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { + u->first = TRUE; + continue; + } + pa_log("Failed to write data to DSP: %s", snd_strerror(err)); return -1; } @@ -222,19 +224,19 @@ static int mmap_write(struct userdata *u) { pa_log_debug("snd_pcm_mmap_commit: %s", snd_strerror(err)); - if (err == -EPIPE) { - pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); - u->first = TRUE; - } - - if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) - continue; - if (err == -EAGAIN) { pa_log_debug("EAGAIN"); return work_done; } + if (err == -EPIPE) + pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); + + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { + u->first = TRUE; + continue; + } + pa_log("Failed to write data to DSP: %s", snd_strerror(err)); return -1; } @@ -264,6 +266,8 @@ static int unix_write(struct userdata *u) { snd_pcm_sframes_t n, frames; int err; + snd_pcm_hwsync(u->pcm_handle); + if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { pa_log("Failed to query DSP status data: %s", snd_strerror(err)); return -1; @@ -300,15 +304,17 @@ static int unix_write(struct userdata *u) { if (PA_UNLIKELY(frames < 0)) { + if (frames == -EAGAIN) { + pa_log_debug("EAGAIN"); + return work_done; + } + if (frames == -EPIPE) pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); - if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) + if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) { + u->first = TRUE; continue; - - if (frames == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; } pa_log("Failed to write data to DSP: %s", snd_strerror(frames)); @@ -332,7 +338,7 @@ static int unix_write(struct userdata *u) { } } -static int update_smoother(struct userdata *u) { +static void update_smoother(struct userdata *u) { snd_pcm_sframes_t delay = 0; int64_t frames; int err; @@ -343,11 +349,12 @@ static int update_smoother(struct userdata *u) { /* Let's update the time smoother */ + snd_pcm_hwsync(u->pcm_handle); snd_pcm_avail_update(u->pcm_handle); if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { - pa_log("Failed to get delay: %s", snd_strerror(err)); - return -1; + pa_log_warn("Failed to get delay: %s", snd_strerror(err)); + return; } frames = u->frame_index - delay; @@ -357,8 +364,6 @@ static int update_smoother(struct userdata *u) { now1 = pa_rtclock_usec(); now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec); pa_smoother_put(u->smoother, now1, now2); - - return 0; } static pa_usec_t sink_get_latency(struct userdata *u) { @@ -814,6 +819,7 @@ static void thread_func(void *userdata) { frames = u->sink->thread_info.rewind_nbytes / u->frame_size; + snd_pcm_hwsync(u->pcm_handle); if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) { pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused)); goto fail; @@ -867,12 +873,11 @@ static void thread_func(void *userdata) { pa_smoother_resume(u->smoother, pa_rtclock_usec()); } - if (update_smoother(u) < 0) - goto fail; + update_smoother(u); } if (u->use_tsched) { - pa_usec_t usec, cusec, sleep_usec; + pa_usec_t usec, cusec; /* OK, the playback buffer is now full, let's * calculate when to wake up next */ @@ -887,19 +892,14 @@ static void thread_func(void *userdata) { pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); - sleep_usec = PA_MIN(usec, cusec); - - pa_log_debug("Waking up in %0.2fms (smaller value).", (double) sleep_usec / PA_USEC_PER_MSEC); - /* We don't trust the conversion, so we wake up whatever comes first */ - pa_rtpoll_set_timer_relative(u->rtpoll, sleep_usec); + pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); } - } else if (u->use_tsched) { + } else if (u->use_tsched) /* OK, we're in an invalid state, let's disable our timers */ pa_rtpoll_set_timer_disabled(u->rtpoll); - } /* Hmm, nothing to do. Let's sleep */ if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) @@ -923,6 +923,7 @@ static void thread_func(void *userdata) { } if (revents & (POLLERR|POLLNVAL|POLLHUP)) { + snd_pcm_state_t state; if (revents & POLLERR) pa_log_warn("Got POLLERR from ALSA"); @@ -931,9 +932,12 @@ static void thread_func(void *userdata) { if (revents & POLLHUP) pa_log_warn("Got POLLHUP from ALSA"); + state = snd_pcm_state(u->pcm_handle); + pa_log_warn("PCM state is %s", snd_pcm_state_name(state)); + /* Try to recover from this error */ - switch (snd_pcm_state(u->pcm_handle)) { + switch (state) { case SND_PCM_STATE_XRUN: if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) { @@ -959,6 +963,8 @@ static void thread_func(void *userdata) { } break; } + + u->first = TRUE; } pa_log_debug("alsa revents = %i", revents); @@ -1132,14 +1138,25 @@ int pa__init(pa_module*m) { if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0) found = TRUE; - else if (dev_id) { - char *md = pa_sprintf_malloc("hw:%s", dev_id); + else { + snd_pcm_info_t *info; + + snd_pcm_info_alloca(&info); + + if (snd_pcm_info(u->pcm_handle, info) >= 0) { + char *md; + int card; - if (strcmp(u->device_name, md)) - if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0) - found = TRUE; + if ((card = snd_pcm_info_get_card(info)) >= 0) { - pa_xfree(md); + md = pa_sprintf_malloc("hw:%i", card); + + if (strcmp(u->device_name, md)) + if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0) + found = TRUE; + pa_xfree(md); + } + } } if (found) @@ -1304,6 +1321,8 @@ int pa__init(pa_module*m) { } else u->mixer_fdl = NULL; + pa_alsa_dump(u->pcm_handle); + if (!(u->thread = pa_thread_new(thread_func, u))) { pa_log("Failed to create thread."); goto fail; diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 901e3427..a074bc65 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -141,18 +141,26 @@ static int mmap_read(struct userdata *u) { pa_memchunk chunk; void *p; + snd_pcm_hwsync(u->pcm_handle); + if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { + pa_log_debug("snd_pcm_avail_update: %s", snd_strerror(n)); + + if (err == -EAGAIN) { + pa_log_debug("EAGAIN"); + return work_done; + } + if (n == -EPIPE) pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); - if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) + if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) { + snd_pcm_start(u->pcm_handle); continue; + } - if (err == -EAGAIN) - return work_done; - - pa_log("snd_pcm_avail_update: %s", snd_strerror(err)); + pa_log("snd_pcm_recover: %s", snd_strerror(err)); return -1; } @@ -163,14 +171,20 @@ static int mmap_read(struct userdata *u) { if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { + pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err)); + + if (err == -EAGAIN) { + pa_log_debug("EAGAIN"); + return work_done; + } + if (err == -EPIPE) pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); - if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { + snd_pcm_start(u->pcm_handle); continue; - - if (err == -EAGAIN) - return work_done; + } pa_log("Failed to write data to DSP: %s", snd_strerror(err)); return -1; @@ -198,14 +212,20 @@ static int mmap_read(struct userdata *u) { if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { + pa_log_debug("snd_pcm_mmap_commit: %s", snd_strerror(err)); + + if (err == -EAGAIN) { + pa_log_debug("EAGAIN"); + return work_done; + } + if (err == -EPIPE) pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); - if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { + snd_pcm_start(u->pcm_handle); continue; - - if (err == -EAGAIN) - return work_done; + } pa_log("Failed to write data to DSP: %s", snd_strerror(err)); return -1; @@ -215,6 +235,8 @@ static int mmap_read(struct userdata *u) { u->frame_index += frames; + pa_log_debug("read %llu frames", (unsigned long long) frames); + if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) return work_done; } @@ -235,6 +257,8 @@ static int unix_read(struct userdata *u) { int err; pa_memchunk chunk; + snd_pcm_hwsync(u->pcm_handle); + if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { pa_log("Failed to query DSP status data: %s", snd_strerror(err)); return -1; @@ -264,12 +288,18 @@ static int unix_read(struct userdata *u) { if (PA_UNLIKELY(frames < 0)) { pa_memblock_unref(chunk.memblock); + if (frames == -EAGAIN) { + pa_log_debug("EAGAIN"); + return work_done; + } + + if (frames == -EPIPE) + pa_log_debug("snd_pcm_avail_update: Buffer overrun!"); + if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) + snd_pcm_start(u->pcm_handle); continue; - if (frames == -EAGAIN) - return work_done; - pa_log("Failed to read data from DSP: %s", snd_strerror(frames)); return -1; } @@ -289,7 +319,7 @@ static int unix_read(struct userdata *u) { } } -static int update_smoother(struct userdata *u) { +static void update_smoother(struct userdata *u) { snd_pcm_sframes_t delay = 0; int64_t frames; int err; @@ -300,11 +330,12 @@ static int update_smoother(struct userdata *u) { /* Let's update the time smoother */ + snd_pcm_hwsync(u->pcm_handle); snd_pcm_avail_update(u->pcm_handle); if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { - pa_log("Failed to get delay: %s", snd_strerror(err)); - return -1; + pa_log_warn("Failed to get delay: %s", snd_strerror(err)); + return; } frames = u->frame_index + delay; @@ -312,8 +343,6 @@ static int update_smoother(struct userdata *u) { now1 = pa_rtclock_usec(); now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec); pa_smoother_put(u->smoother, now1, now2); - - return 0; } static pa_usec_t source_get_latency(struct userdata *u) { @@ -728,8 +757,7 @@ static void thread_func(void *userdata) { } if (work_done) - if (update_smoother(u) < 0) - goto fail; + update_smoother(u); if (u->use_tsched) { pa_usec_t usec, cusec; @@ -750,11 +778,10 @@ static void thread_func(void *userdata) { /* We don't trust the conversion, so we wake up whatever comes first */ pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); } - } else if (u->use_tsched) { + } else if (u->use_tsched) /* OK, we're in an invalid state, let's disable our timers */ pa_rtpoll_set_timer_disabled(u->rtpoll); - } /* Hmm, nothing to do. Let's sleep */ if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) @@ -778,6 +805,7 @@ static void thread_func(void *userdata) { } if (revents & (POLLERR|POLLNVAL|POLLHUP)) { + snd_pcm_state_t state; if (revents & POLLERR) pa_log_warn("Got POLLERR from ALSA"); @@ -786,9 +814,12 @@ static void thread_func(void *userdata) { if (revents & POLLHUP) pa_log_warn("Got POLLHUP from ALSA"); + state = snd_pcm_state(u->pcm_handle); + pa_log_warn("PCM state is %s", snd_pcm_state_name(state)); + /* Try to recover from this error */ - switch (snd_pcm_state(u->pcm_handle)) { + switch (state) { case SND_PCM_STATE_XRUN: if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) { @@ -814,6 +845,8 @@ static void thread_func(void *userdata) { } break; } + + snd_pcm_start(u->pcm_handle); } } } @@ -979,14 +1012,25 @@ int pa__init(pa_module*m) { if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0) found = TRUE; - else if (dev_id) { - char *md = pa_sprintf_malloc("hw:%s", dev_id); + else { + snd_pcm_info_t* info; + + snd_pcm_info_alloca(&info); - if (strcmp(u->device_name, md)) - if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0) - found = TRUE; + if (snd_pcm_info(u->pcm_handle, info) >= 0) { + char *md; + int card; - pa_xfree(md); + if ((card = snd_pcm_info_get_card(info)) >= 0) { + + md = pa_sprintf_malloc("hw:%i", card); + + if (strcmp(u->device_name, md)) + if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0) + found = TRUE; + pa_xfree(md); + } + } } if (found) @@ -1138,6 +1182,8 @@ int pa__init(pa_module*m) { } else u->mixer_fdl = NULL; + pa_alsa_dump(u->pcm_handle); + if (!(u->thread = pa_thread_new(thread_func, u))) { pa_log("Failed to create thread."); goto fail; -- cgit From 3138928504d58c7924b0477b4ec3a6ac6205eaa2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 01:14:33 +0000 Subject: include proplist.h in doxygen docs git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2222 fefdeb5f-60dc-0310-8127-8f9354f1896f --- doxygen/doxygen.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in index 8ccf667a..a45dc2db 100644 --- a/doxygen/doxygen.conf.in +++ b/doxygen/doxygen.conf.in @@ -417,7 +417,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h +INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -- cgit From 0f9e977110fbde443a27ade932ee487e79f0c5b9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 01:14:59 +0000 Subject: bump protocol version git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2223 fefdeb5f-60dc-0310-8127-8f9354f1896f --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 112f6329..76ce327e 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ AC_PREREQ(2.60) m4_define(PA_MAJOR, [0]) m4_define(PA_MINOR, [9]) -m4_define(PA_MICRO, [10]) +m4_define(PA_MICRO, [11]) AC_INIT([pulseaudio], PA_MAJOR.PA_MINOR.PA_MICRO,[mzchyfrnhqvb (at) 0pointer (dot) net]) AC_CONFIG_SRCDIR([src/daemon/main.c]) @@ -37,7 +37,7 @@ AC_SUBST(PA_MAJORMINOR, "PA_MAJOR.PA_MINOR") AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/]) AC_SUBST(PA_API_VERSION, 11) -AC_SUBST(PA_PROTOCOL_VERSION, 12) +AC_SUBST(PA_PROTOCOL_VERSION, 13) # The stable ABI for client applications, for the version info x:y:z # always will hold y=z -- cgit From ad18107223e8c4199b86bb3616677a2b57293fad Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 01:15:23 +0000 Subject: add new latency argument git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2224 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/utils/pacat.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/utils/pacat.c b/src/utils/pacat.c index 68e308d8..e3092138 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -71,6 +71,8 @@ static int channel_map_set = 0; static pa_stream_flags_t flags = 0; +static size_t latency = 0; + /* A shortcut for terminating the application */ static void quit(int ret) { assert(mainloop_api); @@ -229,6 +231,7 @@ static void context_state_callback(pa_context *c, void *userdata) { case PA_CONTEXT_READY: { int r; + pa_buffer_attr buffer_attr; assert(c); assert(!stream); @@ -247,15 +250,21 @@ static void context_state_callback(pa_context *c, void *userdata) { pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL); pa_stream_set_moved_callback(stream, stream_moved_callback, NULL); + if (latency > 0) { + memset(&buffer_attr, 0, sizeof(buffer_attr)); + buffer_attr.tlength = latency; + flags |= PA_STREAM_ADJUST_LATENCY; + } + if (mode == PLAYBACK) { pa_cvolume cv; - if ((r = pa_stream_connect_playback(stream, device, NULL, flags, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) { + if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) { fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(c))); goto fail; } } else { - if ((r = pa_stream_connect_record(stream, device, NULL, flags)) < 0) { + if ((r = pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags)) < 0) { fprintf(stderr, "pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(c))); goto fail; } @@ -478,6 +487,7 @@ static void help(const char *argv0) { " from the sink the stream is being connected to.\n" " --no-remix Don't upmix or downmix channels.\n" " --no-remap Map channels by index instead of name.\n" + " --latency=BYTES Request the specified latency in bytes.\n" , argv0); } @@ -494,7 +504,8 @@ enum { ARG_FIX_RATE, ARG_FIX_CHANNELS, ARG_NO_REMAP, - ARG_NO_REMIX + ARG_NO_REMIX, + ARG_LATENCY }; int main(int argc, char *argv[]) { @@ -523,6 +534,7 @@ int main(int argc, char *argv[]) { {"fix-channels",0, NULL, ARG_FIX_CHANNELS}, {"no-remap", 0, NULL, ARG_NO_REMAP}, {"no-remix", 0, NULL, ARG_NO_REMIX}, + {"latency", 0, NULL, ARG_LATENCY}, {NULL, 0, NULL, 0} }; @@ -601,7 +613,7 @@ int main(int argc, char *argv[]) { case ARG_CHANNELMAP: if (!pa_channel_map_parse(&channel_map, optarg)) { - fprintf(stderr, "Invalid channel map\n"); + fprintf(stderr, "Invalid channel map '%s'\n", optarg); goto quit; } @@ -628,6 +640,13 @@ int main(int argc, char *argv[]) { flags |= PA_STREAM_NO_REMAP_CHANNELS; break; + case ARG_LATENCY: + if (((latency = atoi(optarg))) <= 0) { + fprintf(stderr, "Invallid latency specification '%s'\n", optarg); + goto quit; + } + break; + default: goto quit; } -- cgit From da37a7e93db0f17e03f94f7835d7075faa3ac4ba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 01:16:43 +0000 Subject: export both min and max latency that is configured for a sink; add API for querying the requested latency of a sink/source from the main thread git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2225 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink-input.c | 14 +++++++++++--- src/pulsecore/sink.c | 39 ++++++++++++++++++++++++++++++++++++--- src/pulsecore/sink.h | 7 +++++-- src/pulsecore/source-output.c | 15 ++++++++++++--- src/pulsecore/source.c | 39 ++++++++++++++++++++++++++++++++++++--- src/pulsecore/source.h | 5 ++++- 6 files changed, 104 insertions(+), 15 deletions(-) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 989e2ae5..ddd9980f 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -677,13 +677,21 @@ void pa_sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes /* in the sin pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { pa_sink_input_assert_ref(i); - if (usec < i->sink->min_latency) - usec = i->sink->min_latency; + if (usec > 0) { + + if (i->sink->max_latency > 0 && usec > i->sink->max_latency) + usec = i->sink->max_latency; + + if (i->sink->min_latency > 0 && usec < i->sink->min_latency) + usec = i->sink->min_latency; + } if (PA_SINK_INPUT_LINKED(i->state)) pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); - else + else { i->thread_info.requested_sink_latency = usec; + i->sink->thread_info.requested_latency_valid = FALSE; + } return usec; } diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 39ce83f1..02f568e3 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -188,6 +188,7 @@ pa_sink* pa_sink_new( s->silence = pa_silence_memblock_new(core->mempool, &s->sample_spec, 0); s->min_latency = DEFAULT_MIN_LATENCY; + s->max_latency = s->min_latency; s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); s->thread_info.soft_volume = s->volume; @@ -278,6 +279,8 @@ void pa_sink_put(pa_sink* s) { pa_assert(s->asyncmsgq); pa_assert(s->rtpoll); + pa_assert(!s->min_latency || !s->max_latency || s->min_latency <= s->max_latency); + if (s->get_volume && s->set_volume) s->flags |= PA_SINK_HW_VOLUME_CTRL; else @@ -1132,13 +1135,20 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse * asyncmsgq and rtpoll fields can be changed without * problems */ pa_sink_detach_within_thread(s); - break; + return 0; case PA_SINK_MESSAGE_ATTACH: /* Reattach all streams */ pa_sink_attach_within_thread(s); - break; + return 0; + + case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: { + + pa_usec_t *usec = userdata; + *usec = pa_sink_get_requested_latency_within_thread(s); + return 0; + } case PA_SINK_MESSAGE_GET_LATENCY: case PA_SINK_MESSAGE_MAX: @@ -1223,7 +1233,7 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes) { s->request_rewind(s); } -pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { +pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) { pa_usec_t result = 0; pa_sink_input *i; void *state = NULL; @@ -1239,12 +1249,35 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { (!result || result > i->thread_info.requested_sink_latency)) result = i->thread_info.requested_sink_latency; + if (result > 0) { + if (s->max_latency > 0 && result > s->max_latency) + result = s->max_latency; + + if (s->min_latency > 0 && result < s->min_latency) + result = s->min_latency; + } + s->thread_info.requested_latency = result; s->thread_info.requested_latency_valid = TRUE; return result; } +pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { + pa_usec_t usec = 0; + + pa_sink_assert_ref(s); + pa_assert(PA_SINK_LINKED(s->state)); + + if (!PA_SINK_OPENED(s->state)) + return 0; + + if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) + return 0; + + return usec; +} + void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { pa_sink_input *i; void *state = NULL; diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 23f02c40..2dfd8452 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -91,7 +91,8 @@ struct pa_sink { pa_memblock *silence; - pa_usec_t min_latency; /* we won't go below this latency setting */ + pa_usec_t min_latency; /* we won't go below this latency */ + pa_usec_t max_latency; /* An upper limit for the latencies */ int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */ int (*set_volume)(pa_sink *s); /* dito */ @@ -135,6 +136,7 @@ typedef enum pa_sink_message { PA_SINK_MESSAGE_GET_MUTE, PA_SINK_MESSAGE_SET_MUTE, PA_SINK_MESSAGE_GET_LATENCY, + PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, PA_SINK_MESSAGE_SET_STATE, PA_SINK_MESSAGE_PING, PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, @@ -191,6 +193,7 @@ void pa_sink_attach(pa_sink *s); /* The returned value is supposed to be in the time domain of the sound card! */ pa_usec_t pa_sink_get_latency(pa_sink *s); +pa_usec_t pa_sink_get_requested_latency(pa_sink *s); int pa_sink_update_status(pa_sink*s); int pa_sink_suspend(pa_sink *s, pa_bool_t suspend); @@ -227,7 +230,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse void pa_sink_attach_within_thread(pa_sink *s); void pa_sink_detach_within_thread(pa_sink *s); -pa_usec_t pa_sink_get_requested_latency(pa_sink *s); +pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s); void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index d6c2a2b2..cf576ac9 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -364,13 +364,22 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); - if (usec < o->source->min_latency) - usec = o->source->min_latency; + if (usec > 0) { + + if (o->source->max_latency > 0 && usec > o->source->max_latency) + usec = o->source->max_latency; + + if (o->source->min_latency > 0 && usec < o->source->min_latency) + usec = o->source->min_latency; + + } if (PA_SOURCE_OUTPUT_LINKED(o->state)) pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); - else + else { o->thread_info.requested_source_latency = usec; + o->source->thread_info.requested_latency_valid = FALSE; + } return usec; } diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index b150d8ac..5a4b630b 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -166,6 +166,7 @@ pa_source* pa_source_new( s->refresh_volume = s->refresh_muted = FALSE; s->min_latency = DEFAULT_MIN_LATENCY; + s->max_latency = s->min_latency; s->get_latency = NULL; s->set_volume = NULL; @@ -243,6 +244,8 @@ void pa_source_put(pa_source *s) { pa_assert(s->rtpoll); pa_assert(s->asyncmsgq); + pa_assert(!s->min_latency || !s->max_latency || s->min_latency <= s->max_latency); + if (s->get_volume && s->set_volume) s->flags |= PA_SOURCE_HW_VOLUME_CTRL; else { @@ -616,13 +619,20 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ * asyncmsgq and rtpoll fields can be changed without * problems */ pa_source_detach_within_thread(s); - break; + return 0; case PA_SOURCE_MESSAGE_ATTACH: /* Reattach all streams */ pa_source_attach_within_thread(s); - break; + return 0; + + case PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY: { + + pa_usec_t *usec = userdata; + *usec = pa_source_get_requested_latency_within_thread(s); + return 0; + } case PA_SOURCE_MESSAGE_GET_LATENCY: case PA_SOURCE_MESSAGE_MAX: @@ -683,7 +693,7 @@ void pa_source_attach_within_thread(pa_source *s) { o->attach(o); } -pa_usec_t pa_source_get_requested_latency(pa_source *s) { +pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) { pa_usec_t result = 0; pa_source_output *o; void *state = NULL; @@ -699,12 +709,35 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) { (!result || result > o->thread_info.requested_source_latency)) result = o->thread_info.requested_source_latency; + if (result > 0) { + if (s->max_latency > 0 && result > s->max_latency) + result = s->max_latency; + + if (s->min_latency > 0 && result < s->min_latency) + result = s->min_latency; + } + s->thread_info.requested_latency = result; s->thread_info.requested_latency_valid = TRUE; return result; } +pa_usec_t pa_source_get_requested_latency(pa_source *s) { + pa_usec_t usec; + + pa_source_assert_ref(s); + pa_assert(PA_SOURCE_LINKED(s->state)); + + if (!PA_SOURCE_OPENED(s->state)) + return 0; + + if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) + return 0; + + return usec; +} + void pa_source_invalidate_requested_latency(pa_source *s) { pa_source_assert_ref(s); diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 3ec8320d..ab7236eb 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -92,6 +92,7 @@ struct pa_source { pa_rtpoll *rtpoll; pa_usec_t min_latency; /* we won't go below this latency setting */ + pa_usec_t max_latency; /* An upper limit for the latencies */ int (*set_state)(pa_source*source, pa_source_state_t state); /* may be NULL */ int (*set_volume)(pa_source *s); /* dito */ @@ -127,6 +128,7 @@ typedef enum pa_source_message { PA_SOURCE_MESSAGE_GET_MUTE, PA_SOURCE_MESSAGE_SET_MUTE, PA_SOURCE_MESSAGE_GET_LATENCY, + PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, PA_SOURCE_MESSAGE_SET_STATE, PA_SOURCE_MESSAGE_PING, PA_SOURCE_MESSAGE_ATTACH, @@ -181,6 +183,7 @@ void pa_source_attach(pa_source *s); /* May be called by everyone, from main context */ pa_usec_t pa_source_get_latency(pa_source *s); +pa_usec_t pa_source_get_requested_latency(pa_source *s); int pa_source_update_status(pa_source*s); int pa_source_suspend(pa_source *s, pa_bool_t suspend); @@ -206,7 +209,7 @@ int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa void pa_source_attach_within_thread(pa_source *s); void pa_source_detach_within_thread(pa_source *s); -pa_usec_t pa_source_get_requested_latency(pa_source *s); +pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s); /* To be called exclusively by source output drivers, from IO context */ -- cgit From f3109be9e1df3f7ef723e7122c80e58cc3944951 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 01:17:16 +0000 Subject: show configured latency and its ranges git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2226 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/cli-text.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 8bb567b7..5bf27eec 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -128,6 +128,7 @@ char *pa_sink_list_to_string(pa_core *c) { "\tvolume: <%s>\n" "\tmute: <%i>\n" "\tlatency: <%0.0f usec>\n" + "\tconfigured latency: <%0.0f usec> from range <%0.0f usec> .. <%0.0f usec>\n" "\tmonitor source: <%u>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n" @@ -147,6 +148,7 @@ char *pa_sink_list_to_string(pa_core *c) { pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)), !!pa_sink_get_mute(sink), (double) pa_sink_get_latency(sink), + (double) pa_sink_get_requested_latency(sink), (double) sink->min_latency, (double) sink->max_latency, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX, pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map), @@ -194,6 +196,7 @@ char *pa_source_list_to_string(pa_core *c) { "\tvolume: <%s>\n" "\tmute: <%u>\n" "\tlatency: <%0.0f usec>\n" + "\tconfigured latency: <%0.0f usec> from range <%0.0f usec> .. <%0.0f usec>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n" "\tused by: <%u>\n" @@ -212,6 +215,7 @@ char *pa_source_list_to_string(pa_core *c) { pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)), !!pa_source_get_mute(source), (double) pa_source_get_latency(source), + (double) pa_source_get_requested_latency(source), (double) source->min_latency, (double) source->max_latency, pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map), pa_source_used_by(source), -- cgit From cc1e2654d30ee7547aebd3c1d5b26802e8bea5ff Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 01:18:05 +0000 Subject: init min/max latency properly; fix avail_min updating git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2227 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 10 +++++----- src/modules/module-alsa-source.c | 6 ++++-- src/modules/module-ladspa-sink.c | 2 +- src/modules/module-remap-sink.c | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 70a94ee3..513350fc 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -436,7 +436,7 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { pa_assert(u); - usec = pa_sink_get_requested_latency(u->sink); + usec = pa_sink_get_requested_latency_within_thread(u->sink); if (usec <= 0) usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec); @@ -461,7 +461,7 @@ static void update_hwbuf_unused_frames(struct userdata *u) { pa_assert(u); - if ((usec = pa_sink_get_requested_latency(u->sink)) <= 0) { + if ((usec = pa_sink_get_requested_latency_within_thread(u->sink)) <= 0) { /* Use the full buffer if noone asked us for anything * specific */ u->hwbuf_unused_frames = 0; @@ -499,8 +499,6 @@ static int update_sw_params(struct userdata *u) { } else avail_min = 1; - avail_min = (snd_pcm_uframes_t) -1; - pa_log("setting avail_min=%lu", (unsigned long) avail_min); if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { @@ -1222,8 +1220,10 @@ int pa__init(pa_module*m) { u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0; + u->sink->max_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + if (!use_tsched) - u->sink->min_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + u->sink->min_latency = u->sink->max_latency; pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", nfrags, (long unsigned) u->fragment_size, diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index a074bc65..caaa5458 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -411,7 +411,7 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { pa_assert(u); - usec = pa_source_get_requested_latency(u->source); + usec = pa_source_get_requested_latency_within_thread(u->source); if (usec <= 0) usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); @@ -1093,8 +1093,10 @@ int pa__init(pa_module*m) { u->hw_dB_min = u->hw_dB_max = 0; u->hw_volume_min = u->hw_volume_max = 0; + u->source->max_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + if (!use_tsched) - u->source->min_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + u->source->min_latency = u->source->max_latency; pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", nfrags, (long unsigned) u->fragment_size, diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 1ba2b48a..2342cde9 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -145,7 +145,7 @@ static void sink_update_requested_latency(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency(s); + u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s); pa_sink_invalidate_requested_latency(u->master); } diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 1c97a824..2dc4f2a1 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -123,7 +123,7 @@ static void sink_update_requested_latency(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency(s); + u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s); pa_sink_invalidate_requested_latency(u->master); } -- cgit From fc9d8276dbc1472c8eb726adfd8aa8203a6393fb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 01:19:01 +0000 Subject: remove doxygen \since tag for API changes older than 0.9; properly implement new latency query APIs git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2228 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/channelmap.h | 2 +- src/pulse/context.h | 22 +++++------ src/pulse/def.h | 58 ++++++++++++++-------------- src/pulse/introspect.c | 8 +++- src/pulse/introspect.h | 102 ++++++++++++++++++++++++------------------------- src/pulse/proplist.h | 36 +++++++++-------- src/pulse/scache.h | 4 +- src/pulse/simple.h | 4 +- src/pulse/stream.c | 21 ++++++++-- src/pulse/stream.h | 46 +++++++++++----------- src/pulse/version.h.in | 13 ++++--- src/pulse/volume.h | 10 ++--- 12 files changed, 175 insertions(+), 151 deletions(-) diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index a05e1911..4122a318 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -183,7 +183,7 @@ const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos); /** Make a humand readable string from the specified channel map */ char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map); -/** Parse a channel position list into a channel map structure. \since 0.8.1 */ +/** Parse a channel position list into a channel map structure. */ pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s); /** Compare two channel maps. Return 1 if both match. */ diff --git a/src/pulse/context.h b/src/pulse/context.h index cb2a531f..143508f4 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -173,7 +173,7 @@ 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 */ + * list. \since 0.9.11 */ 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 */ @@ -214,40 +214,40 @@ pa_operation* pa_context_drain(pa_context *c, pa_context_notify_cb_t cb, void *u * returning a success notification */ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata); -/** Set the name of the default sink. \since 0.4 */ +/** Set the name of the default sink. */ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata); -/** Set the name of the default source. \since 0.4 */ +/** Set the name of the default source. */ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata); -/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. \since 0.5 */ +/** Returns 1 when the connection is to a local daemon. Returns negative when no connection has been made yet. */ int pa_context_is_local(pa_context *c); -/** Set a different application name for context on the server. \since 0.5 */ +/** Set a different application name for context on the server. */ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata); -/** Return the server name this context is connected to. \since 0.7 */ +/** Return the server name this context is connected to. */ const char* pa_context_get_server(pa_context *c); -/** Return the protocol version of the library. \since 0.8 */ +/** Return the protocol version of the library. */ uint32_t pa_context_get_protocol_version(pa_context *c); -/** Return the protocol version of the connected server. \since 0.8 */ +/** Return the protocol version of the connected server. */ 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 */ + * used to route streams of the client to the right device. \since 0.9.11 */ 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 */ +/* Update the property list of the client, remove entries. \since 0.9.11 */ 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 */ + * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */ uint32_t pa_context_get_index(pa_context *s); PA_C_DECL_END diff --git a/src/pulse/def.h b/src/pulse/def.h index ae766603..8a83d7a9 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -67,7 +67,7 @@ typedef enum pa_operation_state { /** An invalid index */ #define PA_INVALID_INDEX ((uint32_t) -1) -/** Some special flags for contexts. \since 0.8 */ +/** Some special flags for contexts. */ typedef enum pa_context_flags { PA_CONTEXT_NOAUTOSPAWN = 1 /**< Disabled autospawning of the PulseAudio daemon if required */ } pa_context_flags_t; @@ -80,7 +80,7 @@ typedef enum pa_stream_direction { PA_STREAM_UPLOAD /**< Sample upload stream */ } pa_stream_direction_t; -/** Some special flags for stream connections. \since 0.6 */ +/** Some special flags for stream connections. */ typedef enum pa_stream_flags { PA_STREAM_START_CORKED = 1, /**< Create the stream corked, requiring an explicit pa_stream_cork() call to uncork it. */ PA_STREAM_INTERPOLATE_TIMING = 2, /**< Interpolate the latency for @@ -293,8 +293,8 @@ enum { PA_ERR_MODINITFAILED, /**< Module initialization failed */ PA_ERR_BADSTATE, /**< Bad state */ PA_ERR_NODATA, /**< No data */ - PA_ERR_VERSION, /**< Incompatible protocol version \since 0.8 */ - PA_ERR_TOOLARGE, /**< Data too large \since 0.8.1 */ + PA_ERR_VERSION, /**< Incompatible protocol version */ + PA_ERR_TOOLARGE, /**< Data too large */ PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */ PA_ERR_MAX /**< Not really an error but the first invalid error code */ }; @@ -309,9 +309,9 @@ typedef enum pa_subscription_mask { PA_SUBSCRIPTION_MASK_MODULE = 16, /**< Module events */ PA_SUBSCRIPTION_MASK_CLIENT = 32, /**< Client events */ PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64, /**< Sample cache events */ - PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. \since 0.4 */ - PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. \since 0.5 */ - PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events \since 0.8 */ + PA_SUBSCRIPTION_MASK_SERVER = 128, /**< Other global server changes. */ + PA_SUBSCRIPTION_MASK_AUTOLOAD = 256, /**< Autoload table events. */ + PA_SUBSCRIPTION_MASK_ALL = 511 /**< Catch all events */ } pa_subscription_mask_t; /** Subscription event types, as used by pa_context_subscribe() */ @@ -323,8 +323,8 @@ typedef enum pa_subscription_event_type { PA_SUBSCRIPTION_EVENT_MODULE = 4, /**< Event type: Module */ PA_SUBSCRIPTION_EVENT_CLIENT = 5, /**< Event type: Client */ PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6, /**< Event type: Sample cache item */ - PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4 */ - PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. \since 0.5 */ + PA_SUBSCRIPTION_EVENT_SERVER = 7, /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */ + PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8, /**< Event type: Autoload table changes. */ PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15, /**< A mask to extract the event type from an event value */ PA_SUBSCRIPTION_EVENT_NEW = 0, /**< A new object was created */ @@ -362,12 +362,11 @@ typedef struct pa_timing_info { * detected transport_usec becomes much * more reliable. However, the code that * detects synchronized clocks is very - * limited und unreliable itself. \since - * 0.5 */ + * limited und unreliable itself. */ pa_usec_t sink_usec; /**< Time in usecs a sample takes to be played on the sink. For playback streams and record streams connected to a monitor source. */ - pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. \since 0.5*/ - pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. \since 0.5 */ + pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */ + pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */ int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */ @@ -378,20 +377,19 @@ typedef struct pa_timing_info { * info was current . Only write * commands with SEEK_RELATIVE_ON_READ * and SEEK_RELATIVE_END can corrupt - * write_index. \since 0.8 */ + * write_index. */ int64_t write_index; /**< Current write index into the * playback buffer in bytes. Think twice before * using this for seeking purposes: it * might be out of date a the time you * want to use it. Consider using - * PA_SEEK_RELATIVE instead. \since - * 0.8 */ + * PA_SEEK_RELATIVE instead. */ int read_index_corrupt; /**< Non-zero if read_index is not * up-to-date because a local pause or * flush request that corrupted it has * been issued in the time since this - * latency info was current. \since 0.8 */ + * latency info was current. */ int64_t read_index; /**< Current read index into the * playback buffer in bytes. Think twice before @@ -399,12 +397,12 @@ typedef struct pa_timing_info { * might be out of date a the time you * want to use it. Consider using * PA_SEEK_RELATIVE_ON_READ - * instead. \since 0.8 */ + * instead. */ - pa_usec_t max_sink_usec; /**< The static configure latency for - * the sink. \since 0.9.10 */ - pa_usec_t max_source_usec; /**< The static configure latency for - * the source. \since 0.9.10 */ + pa_usec_t configured_sink_usec; /**< The static configured latency for + * the sink. \since 0.9.11 */ + pa_usec_t configured_source_usec; /**< The static configured latency for + * the source. \since 0.9.11 */ } pa_timing_info; /** A structure for the spawn api. This may be used to integrate auto @@ -413,7 +411,7 @@ typedef struct pa_timing_info { * waitpid() is used on the child's PID. The spawn routine will not * block or ignore SIGCHLD signals, since this cannot be done in a * thread compatible way. You might have to do this in - * prefork/postfork. \since 0.4 */ + * prefork/postfork. */ typedef struct pa_spawn_api { void (*prefork)(void); /**< Is called just before the fork in the parent process. May be NULL. */ void (*postfork)(void); /**< Is called immediately after the fork in the parent process. May be NULL.*/ @@ -426,7 +424,7 @@ typedef struct pa_spawn_api { * passed to the new process. */ } pa_spawn_api; -/** Seek type for pa_stream_write(). \since 0.8*/ +/** Seek type for pa_stream_write(). */ typedef enum pa_seek_mode { PA_SEEK_RELATIVE = 0, /**< Seek relatively to the write index */ PA_SEEK_ABSOLUTE = 1, /**< Seek relatively to the start of the buffer queue */ @@ -434,24 +432,24 @@ typedef enum pa_seek_mode { PA_SEEK_RELATIVE_END = 3 /**< Seek relatively to the current end of the buffer queue. */ } pa_seek_mode_t; -/** Special sink flags. \since 0.8 */ +/** Special sink flags. */ 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_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_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.11 */ + PA_SINK_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */ } pa_sink_flags_t; -/** Special source flags. \since 0.8 */ +/** Special source flags. */ 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_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_HW_MUTE_CTRL = 16, /**< Supports hardware mute control \since 0.9.11 */ + PA_SOURCE_DECIBEL_VOLUME = 32 /**< Volume can be translated to dB with pa_sw_volume_to_dB() \since 0.9.11 */ } pa_source_flags_t; /** A generic free() like callback prototype */ diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 633ee558..9c50b57b 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -167,7 +167,9 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P pa_tagstruct_get_usec(t, &i.latency) < 0 || pa_tagstruct_gets(t, &i.driver) < 0 || pa_tagstruct_getu32(t, &flags) < 0 || - (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) { + (o->context->version >= 13 && + (pa_tagstruct_get_proplist(t, i.proplist) < 0 || + pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) { pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_proplist_free(i.proplist); @@ -286,7 +288,9 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, pa_tagstruct_get_usec(t, &i.latency) < 0 || pa_tagstruct_gets(t, &i.driver) < 0 || pa_tagstruct_getu32(t, &flags) < 0 || - (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) { + (o->context->version >= 13 && + (pa_tagstruct_get_proplist(t, i.proplist) < 0 || + pa_tagstruct_get_usec(t, &i.configured_latency) < 0))) { pa_context_fail(o->context, PA_ERR_PROTOCOL); pa_proplist_free(i.proplist); diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index bc78996f..09cee608 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -207,7 +207,7 @@ PA_C_DECL_BEGIN -#define PA_PORT_SPDIF "spdif" +#define PA_PORT_DIGITAL "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" @@ -222,17 +222,17 @@ typedef struct pa_sink_info { uint32_t index; /**< Index of the sink */ const char *description; /**< Description of this sink */ pa_sample_spec sample_spec; /**< Sample spec of this sink */ - pa_channel_map channel_map; /**< Channel map \since 0.8 */ + pa_channel_map channel_map; /**< Channel map */ uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */ pa_cvolume volume; /**< Volume of the sink */ - int mute; /**< Mute switch of the sink \since 0.8 */ + int mute; /**< Mute switch of the sink */ uint32_t monitor_source; /**< Index of the monitor source connected to this sink */ const char *monitor_source_name; /**< The name of the monitor source */ pa_usec_t latency; /**< Length of queued audio in the output buffer. */ - const char *driver; /**< Driver name. \since 0.8 */ - pa_sink_flags_t flags; /**< Flags \since 0.8 */ + const char *driver; /**< Driver name. */ + pa_sink_flags_t flags; /**< Flags */ pa_proplist *proplist; /**< Property list \since 0.9.11 */ - pa_usec_t max_latency; /**< The static latency this device has been configured to. \since 0.9.11 */ + pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */ } pa_sink_info; /** Callback prototype for pa_context_get_sink_info_by_name() and friends */ @@ -253,10 +253,10 @@ pa_operation* pa_context_set_sink_volume_by_index(pa_context *c, uint32_t idx, c /** Set the volume of a sink device specified by its name */ pa_operation* pa_context_set_sink_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); -/** Set the mute switch of a sink device specified by its index \since 0.8 */ +/** Set the mute switch of a sink device specified by its index */ pa_operation* pa_context_set_sink_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); -/** Set the mute switch of a sink device specified by its name \since 0.8 */ +/** Set the mute switch of a sink device specified by its name */ pa_operation* pa_context_set_sink_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); /** Suspend/Resume a sink. \since 0.9.7 */ @@ -277,17 +277,17 @@ typedef struct pa_source_info { uint32_t index; /**< Index of the source */ const char *description; /**< Description of this source */ pa_sample_spec sample_spec; /**< Sample spec of this source */ - pa_channel_map channel_map; /**< Channel map \since 0.8 */ + pa_channel_map channel_map; /**< Channel map */ uint32_t owner_module; /**< Owning module index, or PA_INVALID_INDEX */ - pa_cvolume volume; /**< Volume of the source \since 0.8 */ - int mute; /**< Mute switch of the sink \since 0.8 */ + pa_cvolume volume; /**< Volume of the source */ + int mute; /**< Mute switch of the sink */ uint32_t monitor_of_sink; /**< If this is a monitor source the index of the owning sink, otherwise PA_INVALID_INDEX */ const char *monitor_of_sink_name; /**< Name of the owning sink, or PA_INVALID_INDEX */ - 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_usec_t max_latency; /**< The static latency this device has been configured to. \since 0.9.11 */ + pa_usec_t latency; /**< Length of filled record buffer of this source. */ + const char *driver; /**< Driver name */ + pa_source_flags_t flags; /**< Flags */ + pa_proplist *proplist; /**< Property list \since 0.9.11 */ + pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */ } pa_source_info; /** Callback prototype for pa_context_get_source_info_by_name() and friends */ @@ -302,16 +302,16 @@ pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa /** Get the complete source list */ pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata); -/** Set the volume of a source device specified by its index \since 0.8 */ +/** Set the volume of a source device specified by its index */ pa_operation* pa_context_set_source_volume_by_index(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); -/** Set the volume of a source device specified by its name \since 0.8 */ +/** Set the volume of a source device specified by its name */ pa_operation* pa_context_set_source_volume_by_name(pa_context *c, const char *name, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata); -/** Set the mute switch of a source device specified by its index \since 0.8 */ +/** Set the mute switch of a source device specified by its index */ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); -/** Set the mute switch of a source device specified by its name \since 0.8 */ +/** Set the mute switch of a source device specified by its name */ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata); /** @} */ @@ -327,9 +327,9 @@ typedef struct pa_server_info { const char *server_version; /**< Version string of the daemon */ const char *server_name; /**< Server package name (usually "pulseaudio") */ pa_sample_spec sample_spec; /**< Default sample specification */ - const char *default_sink_name; /**< Name of default sink. \since 0.4 */ - const char *default_source_name; /**< Name of default sink. \since 0.4*/ - uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. \since 0.8 */ + const char *default_sink_name; /**< Name of default sink. */ + const char *default_source_name; /**< Name of default sink. */ + uint32_t cookie; /**< A random cookie for identifying this instance of PulseAudio. */ } pa_server_info; /** Callback prototype for pa_context_get_server_info() */ @@ -365,10 +365,10 @@ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t /** Callback prototype for pa_context_load_module() */ typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata); -/** Load a module. \since 0.5 */ +/** Load a module. */ pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata); -/** Unload a module. \since 0.5 */ +/** Unload a module. */ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); /** @} */ @@ -382,8 +382,8 @@ typedef struct pa_client_info { uint32_t index; /**< Index of this client */ 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 */ + const char *driver; /**< Driver name */ + pa_proplist *proplist; /**< Property list \since 0.9.11 */ } pa_client_info; /** Callback prototype for pa_context_get_client_info() and firends*/ @@ -395,7 +395,7 @@ pa_operation* pa_context_get_client_info(pa_context *c, uint32_t idx, pa_client_ /** Get the complete client list */ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t cb, void *userdata); -/** Kill a client. \since 0.5 */ +/** Kill a client. */ pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); /** @} */ @@ -416,10 +416,10 @@ typedef struct pa_sink_input_info { pa_cvolume volume; /**< The volume of this sink input */ pa_usec_t buffer_usec; /**< Latency due to buffering in sink input, see pa_latency_info for details */ pa_usec_t sink_usec; /**< Latency of the sink device, see pa_latency_info for details */ - const char *resample_method; /**< Thre resampling method used by this sink input. \since 0.7 */ - const char *driver; /**< Driver name \since 0.8 */ + const char *resample_method; /**< Thre resampling method used by this sink input. */ + const char *driver; /**< Driver name */ int mute; /**< Stream muted \since 0.9.7 */ - pa_proplist *proplist; /**< Property list \since 0.9.10 */ + pa_proplist *proplist; /**< Property list \since 0.9.11 */ } pa_sink_input_info; /** Callback prototype for pa_context_get_sink_input_info() and firends*/ @@ -443,7 +443,7 @@ pa_operation* pa_context_set_sink_input_volume(pa_context *c, uint32_t idx, cons /** Set the mute switch of a sink input stream \since 0.9.7 */ pa_operation* pa_context_set_sink_input_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata); -/** Kill a sink input. \since 0.5 */ +/** Kill a sink input. */ pa_operation* pa_context_kill_sink_input(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); /** @} */ @@ -461,11 +461,11 @@ typedef struct pa_source_output_info { uint32_t source; /**< Index of the connected source */ pa_sample_spec sample_spec; /**< The sample specification of the source output */ pa_channel_map channel_map; /**< Channel map */ - pa_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. \since 0.5 */ - 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_usec_t buffer_usec; /**< Latency due to buffering in the source output, see pa_latency_info for details. */ + pa_usec_t source_usec; /**< Latency of the source device, see pa_latency_info for details. */ + const char *resample_method; /**< Thre resampling method used by this source output. */ + const char *driver; /**< Driver name */ + pa_proplist *proplist; /**< Property list \since 0.9.11 */ } pa_source_output_info; /** Callback prototype for pa_context_get_source_output_info() and firends*/ @@ -489,7 +489,7 @@ pa_operation* pa_context_suspend_source_by_name(pa_context *c, char *source_name /** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */ pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata); -/** Kill a source output. \since 0.5 */ +/** Kill a source output. */ pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata); /** @} */ @@ -504,7 +504,7 @@ typedef struct pa_stat_info { uint32_t memblock_total_size; /**< Currentl total size of allocated memory blocks */ uint32_t memblock_allocated; /**< Allocated memory blocks during the whole lifetime of the daemon */ uint32_t memblock_allocated_size; /**< Total size of all memory blocks allocated during the whole lifetime of the daemon */ - uint32_t scache_size; /**< Total size of all sample cache entries. \since 0.4 */ + uint32_t scache_size; /**< Total size of all sample cache entries. */ } pa_stat_info; /** Callback prototype for pa_context_stat() */ @@ -527,10 +527,10 @@ typedef struct pa_sample_info { pa_sample_spec sample_spec; /**< Sample specification of the sample */ pa_channel_map channel_map; /**< The channel map */ pa_usec_t duration; /**< Duration of this entry */ - 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 */ + uint32_t bytes; /**< Length of this sample in bytes. */ + int lazy; /**< Non-zero when this is a lazy cache entry. */ + const char *filename; /**< In case this is a lazy cache entry, the filename for the sound file to be loaded on demand. */ + pa_proplist *proplist; /**< Property list for this sample. \since 0.9.11 */ } pa_sample_info; /** Callback prototype for pa_context_get_sample_info_by_name() and firends */ @@ -551,7 +551,7 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t /** @{ \name Autoload Entries */ -/** Type of an autoload entry. \since 0.5 */ +/** Type of an autoload entry. */ typedef enum pa_autoload_type { PA_AUTOLOAD_SINK = 0, PA_AUTOLOAD_SOURCE = 1 @@ -559,7 +559,7 @@ typedef enum pa_autoload_type { /** Stores information about autoload entries. Please note that this structure * can be extended as part of evolutionary API updates at any time in - * any new release. \since 0.5 */ + * any new release. */ typedef struct pa_autoload_info { uint32_t index; /**< Index of this autoload entry */ const char *name; /**< Name of the sink or source */ @@ -571,22 +571,22 @@ typedef struct pa_autoload_info { /** Callback prototype for pa_context_get_autoload_info_by_name() and firends */ typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata); -/** Get info about a specific autoload entry. \since 0.6 */ +/** Get info about a specific autoload entry. */ pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata); -/** Get info about a specific autoload entry. \since 0.6 */ +/** Get info about a specific autoload entry. */ pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata); -/** Get the complete list of autoload entries. \since 0.5 */ +/** Get the complete list of autoload entries. */ pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata); -/** Add a new autoload entry. \since 0.5 */ +/** Add a new autoload entry. */ pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata); -/** Remove an autoload entry. \since 0.6 */ +/** Remove an autoload entry. */ pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata); -/** Remove an autoload entry. \since 0.6 */ +/** Remove an autoload entry. */ pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata); /** @} */ diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 4fea23da..21379167 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -102,39 +102,40 @@ #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 */ + * as keys and arbitrary data as values. \since 0.9.11 */ typedef struct pa_proplist pa_proplist; -/** Allocate a property list. \since 0.9.10 */ +/** Allocate a property list. \since 0.9.11 */ pa_proplist* pa_proplist_new(void); -/** Free the property list. \since 0.9.10 */ +/** Free the property list. \since 0.9.11 */ void pa_proplist_free(pa_proplist* p); /** 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 */ + * UTF-8. \since 0.9.11 */ 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 */ + * internal copy of the data passed is made. \since 0.9.11 */ 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*/ + * the data before accessing the property list again. \since 0.9.11 */ const char *pa_proplist_gets(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 */ + * copy of the data before the property list is accessed again. \since + * 0.9.11 */ 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 */ +/** Update mode enum for pa_proplist_update(). \since 0.9.11 */ 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. */ @@ -142,18 +143,18 @@ typedef enum pa_update_mode { } pa_update_mode_t; /** Merge property list "other" into "p", adhering the merge mode as - * specified in "mode". \since 0.9.10 */ + * specified in "mode". \since 0.9.11 */ 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 */ + * specified key name. \since 0.9.11 */ 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 */ + * remove). \since 0.9.11 */ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]); /** Iterate through the property list. The user should allocate a @@ -163,19 +164,22 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]); * 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 */ + * list do not have any particular order. \since 0.9.11 */ const char *pa_proplist_iterate(pa_proplist *p, void **state); -/** Format the property list nicely as a human readable string. \since 0.9.10 */ +/** Format the property list nicely as a human readable string. \since + * 0.9.11 */ 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 */ +/** Returns 1 if an entry for the specified key is existant in the + * property list. \since 0.9.11 */ int pa_proplist_contains(pa_proplist *p, const char *key); -/** Remove all entries from the property list object. \since 0.9.10 */ +/** Remove all entries from the property list object. \since 0.9.11 */ 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 */ +/** Allocate a new property list and copy over every single entry from + * the specific list. \since 0.9.11 */ pa_proplist* pa_proplist_copy(pa_proplist *template); #endif diff --git a/src/pulse/scache.h b/src/pulse/scache.h index a9e0ce8c..46d86a19 100644 --- a/src/pulse/scache.h +++ b/src/pulse/scache.h @@ -81,7 +81,7 @@ 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*/ + * PA_INVALID_INDEX on failure. \since 0.9.11 */ typedef void (*pa_context_play_sample_cb_t)(pa_context *c, uint32_t idx, void *userdata); /** Make this stream a sample upload stream */ @@ -109,7 +109,7 @@ pa_operation* pa_context_play_sample( /** 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 */ + * operation object. \since 0.9.11 */ pa_operation* pa_context_play_sample_with_proplist( pa_context *c /**< Context */, const char *name /**< Name of the sample to play */, diff --git a/src/pulse/simple.h b/src/pulse/simple.h index 0ddd57e0..7fca6ac3 100644 --- a/src/pulse/simple.h +++ b/src/pulse/simple.h @@ -138,10 +138,10 @@ int pa_simple_drain(pa_simple *s, int *error); /** Read some data from the server */ int pa_simple_read(pa_simple *s, void*data, size_t bytes, int *error); -/** Return the playback latency. \since 0.5 */ +/** Return the playback latency. */ pa_usec_t pa_simple_get_latency(pa_simple *s, int *error); -/** Flush the playback buffer. \since 0.5 */ +/** Flush the playback buffer. */ int pa_simple_flush(pa_simple *s, int *error); PA_C_DECL_END diff --git a/src/pulse/stream.c b/src/pulse/stream.c index fb965238..3d794876 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -119,7 +119,8 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa s->record_memblockq = NULL; s->previous_time = 0; - s->timing_info_valid = 0; + memset(&s->timing_info, 0, sizeof(s->timing_info)); + s->timing_info_valid = FALSE; s->read_index_not_before = 0; s->write_index_not_before = 0; @@ -462,7 +463,7 @@ static void request_auto_timing_update(pa_stream *s, int force) { if ((o = pa_stream_update_timing_info(s, NULL, NULL))) { pa_operation_unref(o); - s->auto_timing_update_requested = 1; + s->auto_timing_update_requested = TRUE; } } @@ -666,6 +667,20 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED s->sample_spec = ss; } + if (s->context->version >= 13 && s->direction != PA_STREAM_UPLOAD) { + pa_usec_t usec; + + if (pa_tagstruct_get_usec(t, &usec) < 0) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (s->direction == PA_STREAM_RECORD) + s->timing_info.configured_source_usec = usec; + else + s->timing_info.configured_sink_usec = usec; + } + if (!pa_tagstruct_eof(t)) { pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; @@ -1059,7 +1074,7 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, /* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */ - o->stream->timing_info_valid = 0; + o->stream->timing_info_valid = FALSE; i->write_index_corrupt = 0; i->read_index_corrupt = 0; diff --git a/src/pulse/stream.h b/src/pulse/stream.h index de7c967b..69943a70 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -287,7 +287,7 @@ pa_stream* pa_stream_new( /** Create a new, unconnected stream with the specified name and * sample type, and specify the the initial stream property - * list. \since 0.9.10 */ + * list. \since 0.9.11 */ 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 */, @@ -377,20 +377,20 @@ int pa_stream_write( * data will point to the actual data and length will contain the size * of the data in bytes (which can be less than a complete framgnet). * Use pa_stream_drop() to actually remove the data from the - * buffer. If no data is available will return a NULL pointer \since 0.8 */ + * buffer. If no data is available will return a NULL pointer */ int pa_stream_peek( pa_stream *p /**< The stream to use */, const void **data /**< Pointer to pointer that will point to data */, size_t *bytes /**< The length of the data read in bytes */); /** Remove the current fragment on record streams. It is invalid to do this without first - * calling pa_stream_peek(). \since 0.8 */ + * calling pa_stream_peek(). */ int pa_stream_drop(pa_stream *p); /** Return the number of bytes that may be written using pa_stream_write() */ size_t pa_stream_writable_size(pa_stream *p); -/** Return the number of bytes that may be read using pa_stream_read() \since 0.8 */ +/** Return the number of bytes that may be read using pa_stream_read()*/ size_t pa_stream_readable_size(pa_stream *p); /** Drain a playback stream. Use this for notification when the buffer is empty */ @@ -410,18 +410,18 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void * void pa_stream_set_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata); /** Set the callback function that is called when new data is available from the stream. - * Return the number of bytes read. \since 0.8 */ + * Return the number of bytes read.*/ void pa_stream_set_read_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata); -/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) \since 0.8 */ +/** Set the callback function that is called when a buffer overflow happens. (Only for playback streams) */ void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); -/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) \since 0.8 */ +/** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */ void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); /** Set the callback function that is called whenever a latency * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE - * streams only. (Only for playback streams) \since 0.8.2 */ + * streams only. (Only for playback streams) */ void pa_stream_set_latency_update_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); /** Set the callback function that is called whenever the stream is @@ -441,24 +441,25 @@ 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); -/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. \since 0.3 */ +/** 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); /** Flush the playback buffer of this stream. Most of the time you're - * better off using the parameter delta of pa_stream_write() instead of this - * function. Available on both playback and recording streams. \since 0.3 */ + * better off using the parameter delta of pa_stream_write() instead + * of this function. Available on both playback and recording + * streams. */ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); /** Reenable prebuffering as specified in the pa_buffer_attr - * structure. Available for playback streams only. \since 0.6 */ + * structure. Available for playback streams only. */ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); /** Request immediate start of playback on this stream. This disables - * prebuffering as specified in the pa_buffer_attr - * structure, temporarily. Available for playback streams only. \since 0.3 */ + * prebuffering as specified in the pa_buffer_attr structure, + * 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. \since 0.5 */ +/** 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 @@ -475,13 +476,13 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe * be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be * desirable to deal better with bad estimations of transport * latencies, but may have strange effects if the application is not - * able to deal with time going 'backwards'. \since 0.6 */ + * able to deal with time going 'backwards'. */ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec); /** Return the total stream latency. This function is based on * pa_stream_get_time(). In case the stream is a monitoring stream the * result can be negative, i.e. the captured samples are not yet - * played. In this case *negative is set to 1. \since 0.6 */ + * played. In this case *negative is set to 1. */ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative); /** Return the latest raw timing data structure. The returned pointer @@ -493,13 +494,13 @@ int pa_stream_get_latency(pa_stream *s, pa_usec_t *r_usec, int *negative); * function will fail with PA_ERR_NODATA. Please note that the * write_index member field (and only this field) is updated on each * pa_stream_write() call, not just when a timing update has been - * recieved. \since 0.8 */ + * recieved. */ const pa_timing_info* pa_stream_get_timing_info(pa_stream *s); -/** Return a pointer to the stream's sample specification. \since 0.6 */ +/** Return a pointer to the stream's sample specification. */ const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s); -/** Return a pointer to the stream's channel map. \since 0.8 */ +/** Return a pointer to the stream's channel map. */ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s); /** Return the buffer metrics of the stream. Only valid after the @@ -527,10 +528,11 @@ pa_operation *pa_stream_update_sample_rate(pa_stream *s, uint32_t rate, pa_strea * 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 */ + * this stream to the right device. \since 0.9.11 */ 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 */ +/* Update the property list of the sink input/source output of this + * stream, remove entries. \since 0.9.11 */ 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 diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in index 20c7a9c0..dc0f8e3b 100644 --- a/src/pulse/version.h.in +++ b/src/pulse/version.h.in @@ -8,17 +8,17 @@ Copyright 2004-2006 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB - + PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + PulseAudio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 @@ -39,7 +39,8 @@ a macro and not a function, so it is impossible to get the pointer of it. */ #define pa_get_headers_version() ("@PACKAGE_VERSION@") -/** Return the version of the library the current application is linked to. */ +/** Return the version of the library the current application is + * linked to. */ const char* pa_get_library_version(void); /** The current API version. Version 6 relates to Polypaudio @@ -47,8 +48,8 @@ const char* pa_get_library_version(void); * PA_API_VERSION undefined. */ #define PA_API_VERSION @PA_API_VERSION@ -/** The current protocol version. Version 8 relates to Polypaudio 0.8/PulseAudio 0.9. - * \since 0.8 */ +/** The current protocol version. Version 8 relates to Polypaudio + * 0.8/PulseAudio 0.9. */ #define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@ PA_C_DECL_END diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 004e88c1..0fbf872f 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -151,22 +151,22 @@ pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; /** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE; -/** Convert a decibel value to a volume. This is only valid for software volumes! \since 0.4 */ +/** Convert a decibel value to a volume. This is only valid for software volumes! */ pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST; -/** Convert a volume to a decibel value. This is only valid for software volumes! \since 0.4 */ +/** Convert a volume to a decibel value. This is only valid for software volumes! */ double pa_sw_volume_to_dB(pa_volume_t v) PA_GCC_CONST; -/** Convert a linear factor to a volume. This is only valid for software volumes! \since 0.8 */ +/** Convert a linear factor to a volume. This is only valid for software volumes! */ pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST; -/** Convert a volume to a linear factor. This is only valid for software volumes! \since 0.8 */ +/** Convert a volume to a linear factor. This is only valid for software volumes! */ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST; #ifdef INFINITY #define PA_DECIBEL_MININFTY (-INFINITY) #else -/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). \since 0.4 */ +/** This value is used as minus infinity when using pa_volume_{to,from}_dB(). */ #define PA_DECIBEL_MININFTY (-200) #endif -- cgit From d69aeebc00d1babd40e2101cd94373f7c8dc4c0d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 01:19:43 +0000 Subject: implement server side of new sink/source reconfiguration commands git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2229 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-native.c | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 59d1612c..db52b7f8 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -86,6 +86,7 @@ typedef struct record_stream { pa_source_output *source_output; pa_memblockq *memblockq; size_t fragment_size; + pa_usec_t source_latency; } record_stream; typedef struct output_stream { @@ -107,6 +108,7 @@ typedef struct playback_stream { pa_atomic_t missing; size_t minreq; + pa_usec_t sink_latency; /* Only updated after SINK_INPUT_MESSAGE_UPDATE_LATENCY */ int64_t read_index, write_index; @@ -524,7 +526,7 @@ static record_stream* record_stream_new( *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*1000, &source_output->sample_spec); if (adjust_latency) { - pa_usec_t fragsize_usec, source_latency; + pa_usec_t fragsize_usec; /* So, the user asked us to adjust the latency according to * the what the source can provide. Half the latency will be @@ -533,12 +535,12 @@ static record_stream* record_stream_new( fragsize_usec = pa_bytes_to_usec(*fragsize, &source_output->sample_spec); - source_latency = pa_source_output_set_requested_latency(source_output, fragsize_usec/2); + s->source_latency = pa_source_output_set_requested_latency(source_output, fragsize_usec/2); - if (fragsize_usec >= source_latency*2) - fragsize_usec -= source_latency; + if (fragsize_usec >= s->source_latency*2) + fragsize_usec -= s->source_latency; else - fragsize_usec = source_latency; + fragsize_usec = s->source_latency; *fragsize = pa_usec_to_bytes(fragsize_usec, &source_output->sample_spec); } @@ -780,7 +782,7 @@ static playback_stream* playback_stream_new( *prebuf = *tlength; if (adjust_latency) { - pa_usec_t tlength_usec, minreq_usec, sink_latency; + pa_usec_t tlength_usec, minreq_usec; /* So, the user asked us to adjust the latency according to * the what the sink can provide. Half the latency will be @@ -790,17 +792,17 @@ static playback_stream* playback_stream_new( tlength_usec = pa_bytes_to_usec(*tlength, &sink_input->sample_spec); minreq_usec = pa_bytes_to_usec(*minreq, &sink_input->sample_spec); - sink_latency = pa_sink_input_set_requested_latency(sink_input, tlength_usec/2); + s->sink_latency = pa_sink_input_set_requested_latency(sink_input, tlength_usec/2); - if (tlength_usec >= sink_latency*2) - tlength_usec -= sink_latency; + if (tlength_usec >= s->sink_latency*2) + tlength_usec -= s->sink_latency; else - tlength_usec = sink_latency; + tlength_usec = s->sink_latency; - if (minreq_usec >= sink_latency*2) - minreq_usec -= sink_latency; + if (minreq_usec >= s->sink_latency*2) + minreq_usec -= s->sink_latency; else - minreq_usec = sink_latency; + minreq_usec = s->sink_latency; *tlength = pa_usec_to_bytes(tlength_usec, &sink_input->sample_spec); *minreq = pa_usec_to_bytes(minreq_usec, &sink_input->sample_spec); @@ -1450,6 +1452,9 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC pa_tagstruct_put_boolean(reply, pa_sink_get_state(s->sink_input->sink) == PA_SINK_SUSPENDED); } + if (c->version >= 13) + pa_tagstruct_put_usec(reply, s->sink_latency); + pa_pstream_send_tagstruct(c->pstream, reply); } @@ -1651,6 +1656,9 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_SUSPENDED); } + if (c->version >= 13) + pa_tagstruct_put_usec(reply, s->source_latency); + pa_pstream_send_tagstruct(c->pstream, reply); } @@ -2258,8 +2266,10 @@ static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_in pa_tagstruct_puts(t, s->driver); if (c->version >= 11) pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s)); - if (c->version >= 13) + if (c->version >= 13) { pa_tagstruct_put_proplist(t, s->proplist); + pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(s->sink)); + } } static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) { @@ -2282,8 +2292,10 @@ static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sour pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s))); pa_tagstruct_puts(t, s->driver); - if (c->version >= 13) + if (c->version >= 13) { pa_tagstruct_put_proplist(t, s->proplist); + pa_tagstruct_put_usec(t, pa_source_get_requested_latency(s->source)); + } } static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entry *e) { -- cgit From 39afb140935ffb9eb47142ddb125bc6488e06fd2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 13:49:04 +0000 Subject: add new pa_proplist_setf() API function git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2230 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/proplist.c | 22 ++++++++++++++++++++++ src/pulse/proplist.h | 9 +++++++++ 2 files changed, 31 insertions(+) diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 31fd10d5..33bd274f 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -102,6 +102,28 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) { return 0; } +/** Will accept only valid UTF-8 */ +int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) { + va_list ap; + int r; + char *t; + + pa_assert(p); + pa_assert(key); + + if (!property_name_valid(key) || !pa_utf8_valid(format)) + return -1; + + va_start(ap, format); + t = pa_vsprintf_malloc(format, ap); + va_end(ap); + + r = pa_proplist_sets(p, key, t); + + pa_xfree(t); + return r; +} + int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) { struct property *prop; pa_bool_t add = FALSE; diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 21379167..7c3423fa 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -100,6 +100,8 @@ #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" +#define PA_PROP_DEVICE_BUFFER_NFRAGMENTS "device.buffer.nfragments" +#define PA_PROP_DEVICE_BUFFER_FRAGMENT_SIZE "device.buffer.fragment_size" /** A property list object. Basically a dictionary with UTF-8 strings * as keys and arbitrary data as values. \since 0.9.11 */ @@ -117,6 +119,13 @@ void pa_proplist_free(pa_proplist* p); * UTF-8. \since 0.9.11 */ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value); +/** 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. The data can be passed as printf()-style format string with + * arguments. \since 0.9.11 */ +int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) PA_GCC_PRINTF_ATTR(3,4); + /** 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.11 */ -- cgit From 5d7128abf992c1b7fb84f0f9e7eea10d7f70aa01 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 9 Apr 2008 13:49:37 +0000 Subject: add new describe-module CLI command git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2231 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/cli-command.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index ca4be593..ced56c24 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -90,6 +90,7 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); +static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail); @@ -136,6 +137,7 @@ static const struct command commands[] = { { "list", pa_cli_command_info, NULL, 1 }, { "load-module", pa_cli_command_load, "Load a module (args: name, arguments)", 3}, { "unload-module", pa_cli_command_unload, "Unload a module (args: index)", 2}, + { "describe-module", pa_cli_command_describe, "Describe a module (arg: name)", 2}, { "set-sink-volume", pa_cli_command_sink_volume, "Set the volume of a sink (args: index|name, volume)", 3}, { "set-sink-input-volume", pa_cli_command_sink_input_volume, "Set the volume of a sink input (args: index, volume)", 3}, { "set-source-volume", pa_cli_command_source_volume, "Set the volume of a source (args: index|name, volume)", 3}, @@ -419,6 +421,45 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa return 0; } +static int pa_cli_command_describe(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { + const char *name; + pa_modinfo *i; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + + if (!(name = pa_tokenizer_get(t, 1))) { + pa_strbuf_puts(buf, "You need to specify the module name.\n"); + return -1; + } + + if ((i = pa_modinfo_get_by_name(name))) { + + pa_strbuf_printf(buf, "Name: %s\n", name); + + if (!i->description && !i->version && !i->author && !i->usage) + pa_strbuf_printf(buf, "No module information available\n"); + else { + if (i->version) + pa_strbuf_printf(buf, "Version: %s\n", i->version); + if (i->description) + pa_strbuf_printf(buf, "Description: %s\n", i->description); + if (i->author) + pa_strbuf_printf(buf, "Author: %s\n", i->author); + if (i->usage) + pa_strbuf_printf(buf, "Usage: %s\n", i->usage); + pa_strbuf_printf(buf, "Load Once: %s\n", pa_yes_no(i->load_once)); + } + + pa_modinfo_free(i); + } else + pa_strbuf_puts(buf, "Failed to open module.\n"); + + return 0; +} + static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) { const char *n, *v; pa_sink *sink; -- cgit From bb9792a616c5e2cae9526974054325854a75b0f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:19:35 +0000 Subject: move gccmacro from pulsecore/ to pulse/ git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2232 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/gccmacro.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++ src/pulsecore/gccmacro.h | 90 ------------------------------------------------ 2 files changed, 89 insertions(+), 90 deletions(-) create mode 100644 src/pulse/gccmacro.h delete mode 100644 src/pulsecore/gccmacro.h diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h new file mode 100644 index 00000000..5a2a250f --- /dev/null +++ b/src/pulse/gccmacro.h @@ -0,0 +1,89 @@ +#ifndef foopulsegccmacrohfoo +#define foopulsegccmacrohfoo + +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef __GNUC__ +#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) +#else +/** If we're in GNU C, use some magic for detecting invalid format strings */ +#define PA_GCC_PRINTF_ATTR(a,b) +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define PA_GCC_SENTINEL __attribute__ ((sentinel)) +#else +/** Macro for usage of GCC's sentinel compilation warnings */ +#define PA_GCC_SENTINEL +#endif + +#ifdef __GNUC__ +#define PA_GCC_NORETURN __attribute__((noreturn)) +#else +/** Macro for no-return functions */ +#define PA_GCC_NORETURN +#endif + +#ifdef __GNUC__ +#define PA_GCC_UNUSED __attribute__ ((unused)) +#else +/** Macro for not used parameter */ +#define PA_GCC_UNUSED +#endif + +#ifdef __GNUC__ +#define PA_GCC_DESTRUCTOR __attribute__ ((destructor)) +#else +/** Call this function when process terminates */ +#define PA_GCC_DESTRUCTOR +#endif + +#ifndef PA_GCC_PURE +#ifdef __GNUC__ +#define PA_GCC_PURE __attribute__ ((pure)) +#else +/** This function's return value depends only the arguments list and global state **/ +#define PA_GCC_PURE +#endif +#endif + +#ifndef PA_GCC_CONST +#ifdef __GNUC__ +#define PA_GCC_CONST __attribute__ ((const)) +#else +/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/ +#define PA_GCC_CONST +#endif +#endif + +#ifndef PA_GCC_DEPRECATED +#ifdef __GNUC__ +#define PA_GCC_DEPRECATED __attribute__ ((deprecated)) +#else +/** This function is deprecated **/ +#define PA_GCC_DEPRECATED +#endif +#endif + +#endif diff --git a/src/pulsecore/gccmacro.h b/src/pulsecore/gccmacro.h deleted file mode 100644 index f94a8c45..00000000 --- a/src/pulsecore/gccmacro.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef foopulsegccmacrohfoo -#define foopulsegccmacrohfoo - -/* $Id$ */ - -/*** - This file is part of PulseAudio. - - Copyright 2004-2006 Lennart Poettering - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef __GNUC__ -#define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) -#else -/** If we're in GNU C, use some magic for detecting invalid format strings */ -#define PA_GCC_PRINTF_ATTR(a,b) -#endif - -#if defined(__GNUC__) && (__GNUC__ >= 4) -#define PA_GCC_SENTINEL __attribute__ ((sentinel)) -#else -/** Macro for usage of GCC's sentinel compilation warnings */ -#define PA_GCC_SENTINEL -#endif - -#ifdef __GNUC__ -#define PA_GCC_NORETURN __attribute__((noreturn)) -#else -/** Macro for no-return functions */ -#define PA_GCC_NORETURN -#endif - -#ifdef __GNUC__ -#define PA_GCC_UNUSED __attribute__ ((unused)) -#else -/** Macro for not used parameter */ -#define PA_GCC_UNUSED -#endif - -#ifdef __GNUC__ -#define PA_GCC_DESTRUCTOR __attribute__ ((destructor)) -#else -/** Call this function when process terminates */ -#define PA_GCC_DESTRUCTOR -#endif - -#ifndef PA_GCC_PURE -#ifdef __GNUC__ -#define PA_GCC_PURE __attribute__ ((pure)) -#else -/** This function's return value depends only the arguments list and global state **/ -#define PA_GCC_PURE -#endif -#endif - -#ifndef PA_GCC_CONST -#ifdef __GNUC__ -#define PA_GCC_CONST __attribute__ ((const)) -#else -/** This function's return value depends only the arguments list (stricter version of PA_GCC_PURE) **/ -#define PA_GCC_CONST -#endif -#endif - -#ifndef PA_LIKELY -#ifdef __GNUC__ -#define PA_LIKELY(x) (__builtin_expect(!!(x),1)) -#define PA_UNLIKELY(x) (__builtin_expect((x),0)) -#else -#define PA_LIKELY(x) (x) -#define PA_UNLIKELY(x) (x) -#endif -#endif - -#endif -- cgit From e1c1a782b65e5e207b72e88f3ae04720a743c7e8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:20:33 +0000 Subject: fix proplist serialization git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2233 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/tagstruct.c | 13 ++++--------- src/pulsecore/tagstruct.h | 3 +++ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c index 92bace27..fb412a4a 100644 --- a/src/pulsecore/tagstruct.c +++ b/src/pulsecore/tagstruct.c @@ -570,10 +570,11 @@ int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) { return -1; saved_rindex = t->rindex; + t->rindex++; for (;;) { const char *k; - void *d; + const void *d; uint32_t length; if (pa_tagstruct_gets(t, &k) < 0) @@ -588,17 +589,11 @@ int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p) { if (length > MAX_TAG_SIZE) goto fail; - d = pa_xmalloc(length); - - if (pa_tagstruct_get_arbitrary(t, d, length) < 0) + if (pa_tagstruct_get_arbitrary(t, &d, length) < 0) goto fail; - if (pa_proplist_set(p, k, d, length) < 0) { - pa_xfree(d); + if (pa_proplist_set(p, k, d, length) < 0) goto fail; - } - - pa_xfree(d); } return 0; diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h index 8846b301..8699e6c8 100644 --- a/src/pulsecore/tagstruct.h +++ b/src/pulsecore/tagstruct.h @@ -33,6 +33,9 @@ #include #include #include +#include + +#include typedef struct pa_tagstruct pa_tagstruct; -- cgit From 6cddf6135cfa86f15a2b43f6e7ae7d16f8711241 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:21:13 +0000 Subject: add new API pa_rtclock_from_wallclock() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2234 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/rtclock.c | 21 +++++++++++++++++++++ src/pulsecore/rtclock.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c index 07d776e4..e74e5243 100644 --- a/src/pulsecore/rtclock.c +++ b/src/pulsecore/rtclock.c @@ -96,3 +96,24 @@ pa_usec_t pa_rtclock_usec(void) { return pa_timeval_load(pa_rtclock_get(&tv)); } + +struct timeval* pa_rtclock_from_wallclock(struct timeval *tv) { + +#ifdef HAVE_CLOCK_GETTIME + struct timeval wc_now, rt_now; + + pa_gettimeofday(&wc_now); + pa_rtclock_get(&rt_now); + + pa_assert(tv); + + if (pa_timeval_cmp(&wc_now, tv) < 0) + pa_timeval_add(&rt_now, pa_timeval_diff(tv, &wc_now)); + else + pa_timeval_sub(&rt_now, pa_timeval_diff(&wc_now, tv)); + + *tv = rt_now; +#endif + + return tv; +} diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h index f0360af3..f68ad761 100644 --- a/src/pulsecore/rtclock.h +++ b/src/pulsecore/rtclock.h @@ -40,4 +40,6 @@ pa_bool_t pa_rtclock_hrtimer(void); /* timer with a resolution better than this are considered high-resolution */ #define PA_HRTIMER_THRESHOLD_USEC 10 +struct timeval* pa_rtclock_from_wallclock(struct timeval *tv); + #endif -- cgit From c61c3b614134a76e167136415d6de52752180886 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:22:02 +0000 Subject: increase version of required ALSA to 1.0.16. check for gdbm git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2235 fefdeb5f-60dc-0310-8127-8f9354f1896f --- configure.ac | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 76ce327e..ca7565de 100644 --- a/configure.ac +++ b/configure.ac @@ -377,6 +377,9 @@ AC_SEARCH_LIBS([connect], [socket]) # build, disabling its ability to make dlls. AC_CHECK_FUNCS([getopt_long], [], [AC_CHECK_LIB([iberty], [getopt_long])]) +AC_CHECK_LIB(gdbm, gdbm_open) +AC_CHECK_HEADERS(gdbm.h) + #### Check for functions #### # ISO @@ -592,7 +595,7 @@ AC_ARG_ENABLE([alsa], [alsa=auto]) if test "x${alsa}" != xno ; then - PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.0 ], + PKG_CHECK_MODULES(ASOUNDLIB, [ alsa >= 1.0.16 ], [ HAVE_ALSA=1 AC_DEFINE([HAVE_ALSA], 1, [Have ALSA?]) -- cgit From e084e4b5de6d763b8827095231a860926e12b120 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:22:37 +0000 Subject: add new module module-device-restore git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2236 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 8 + src/modules/module-device-restore.c | 348 ++++++++++++++++++++++++++++++++++++ 2 files changed, 356 insertions(+) create mode 100644 src/modules/module-device-restore.c diff --git a/src/Makefile.am b/src/Makefile.am index e537e97b..7139d143 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -999,6 +999,7 @@ modlibexec_LTLIBRARIES += \ module-null-sink.la \ module-detect.la \ module-volume-restore.la \ + module-device-restore.la \ module-default-device-restore.la \ module-rescue-streams.la \ module-suspend-on-idle.la \ @@ -1169,6 +1170,7 @@ SYMDEF_FILES = \ modules/module-jack-sink-symdef.h \ modules/module-jack-source-symdef.h \ modules/module-volume-restore-symdef.h \ + modules/module-device-restore-symdef.h \ modules/module-default-device-restore-symdef.h \ modules/module-rescue-streams-symdef.h \ modules/module-suspend-on-idle-symdef.h \ @@ -1409,6 +1411,12 @@ module_volume_restore_la_LDFLAGS = -module -avoid-version module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la module_volume_restore_la_CFLAGS = $(AM_CFLAGS) +# Device volume restore module +module_device_restore_la_SOURCES = modules/module-device-restore.c +module_device_restore_la_LDFLAGS = -module -avoid-version +module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lgdbm +module_device_restore_la_CFLAGS = $(AM_CFLAGS) + # Default sink/source restore module module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c module_default_device_restore_la_LDFLAGS = -module -avoid-version diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c new file mode 100644 index 00000000..27c69f31 --- /dev/null +++ b/src/modules/module-device-restore.c @@ -0,0 +1,348 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2006 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "module-device-restore-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(TRUE); + +#define SAVE_INTERVAL 10 + +static const char* const valid_modargs[] = { + NULL, +}; + +struct userdata { + pa_core *core; + pa_subscription *subscription; + pa_hook_slot *sink_fixate_hook_slot, *source_fixate_hook_slot; + pa_time_event *save_time_event; + GDBM_FILE gdbm_file; +}; + +struct entry { + pa_cvolume volume; + int muted; +}; + +static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + + pa_assert(a); + pa_assert(e); + pa_assert(tv); + pa_assert(u); + + pa_assert(e == u->save_time_event); + u->core->mainloop->time_free(u->save_time_event); + u->save_time_event = NULL; + + gdbm_sync(u->gdbm_file); + pa_log_info("Synced."); +} + +static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + struct userdata *u = userdata; + struct entry entry; + char *name; + datum key, data; + + pa_assert(c); + pa_assert(u); + + if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) && + t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) && + t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) && + t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)) + return; + + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { + pa_sink *sink; + + if (!(sink = pa_idxset_get_by_index(c->sinks, idx))) + return; + + name = pa_sprintf_malloc("sink:%s", sink->name); + entry.volume = *pa_sink_get_volume(sink); + entry.muted = pa_sink_get_mute(sink); + + } else { + pa_source *source; + + pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE); + + if (!(source = pa_idxset_get_by_index(c->sources, idx))) + return; + + name = pa_sprintf_malloc("source:%s", source->name); + entry.volume = *pa_source_get_volume(source); + entry.muted = pa_source_get_mute(source); + } + + key.dptr = name; + key.dsize = strlen(name); + + data = gdbm_fetch(u->gdbm_file, key); + + if (data.dptr) { + + if (data.dsize == sizeof(struct entry)) { + struct entry *old = (struct entry*) data.dptr; + + if (pa_cvolume_valid(&old->volume)) { + + if (pa_cvolume_equal(&old->volume, &entry.volume) && + !old->muted == !entry.muted) { + + pa_xfree(data.dptr); + pa_xfree(name); + return; + } + } else + pa_log_warn("Invalid volume stored in database for device %s", name); + + } else + pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); + + pa_xfree(data.dptr); + } + + data.dptr = (void*) &entry; + data.dsize = sizeof(entry); + + pa_log_info("Storing volume/mute for device %s.", name); + + gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE); + + if (!u->save_time_event) { + struct timeval tv; + pa_gettimeofday(&tv); + tv.tv_sec += SAVE_INTERVAL; + u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u); + } + + pa_xfree(name); +} + +static struct entry* read_entry(struct userdata *u, char *name) { + datum key, data; + struct entry *e; + + pa_assert(u); + pa_assert(name); + + key.dptr = name; + key.dsize = strlen(name); + + data = gdbm_fetch(u->gdbm_file, key); + + if (!data.dptr) + goto fail; + + if (data.dsize != sizeof(struct entry)) { + pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); + goto fail; + } + + e = (struct entry*) data.dptr; + + if (!(pa_cvolume_valid(&e->volume))) { + pa_log_warn("Invalid volume stored in database for device %s", name); + goto fail; + } + + return e; + +fail: + + pa_xfree(data.dptr); + return NULL; +} + + +static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(new_data); + + name = pa_sprintf_malloc("sink:%s", new_data->name); + + if ((e = read_entry(u, name))) { + + if (e->volume.channels == new_data->sample_spec.channels) { + pa_log_info("Restoring volume for sink %s.", new_data->name); + pa_sink_new_data_set_volume(new_data, &e->volume); + } + + pa_log_info("Restoring mute state for sink %s.", new_data->name); + pa_sink_new_data_set_muted(new_data, e->muted); + pa_xfree(e); + } + + pa_xfree(name); + + return PA_HOOK_OK; +} + +static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) { + char *name; + struct entry *e; + + pa_assert(new_data); + + name = pa_sprintf_malloc("source:%s", new_data->name); + + if ((e = read_entry(u, name))) { + + if (e->volume.channels == new_data->sample_spec.channels) { + pa_log_info("Restoring volume for source %s.", new_data->name); + pa_source_new_data_set_volume(new_data, &e->volume); + } + + pa_log_info("Restoring mute state for source %s.", new_data->name); + pa_source_new_data_set_muted(new_data, e->muted); + pa_xfree(e); + } + + pa_xfree(name); + + return PA_HOOK_OK; +} + +int pa__init(pa_module*m) { + pa_modargs *ma = NULL; + struct userdata *u; + char *fname, *state_dir; + char hn[256]; + pa_sink *sink; + pa_source *source; + uint32_t idx; + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + u = pa_xnew(struct userdata, 1); + u->core = m->core; + u->save_time_event = NULL; + + u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u); + + u->sink_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_FIXATE], (pa_hook_cb_t) sink_fixate_hook_callback, u); + u->source_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], (pa_hook_cb_t) source_fixate_hook_callback, u); + + m->userdata = u; + + if (!pa_get_host_name(hn, sizeof(hn))) + goto fail; + + if (!(state_dir = pa_get_state_dir())) + goto fail; + + fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", state_dir, hn); + pa_xfree(state_dir); + + if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) { + pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno)); + pa_xfree(fname); + goto fail; + } + + pa_log_info("Sucessfully opened database file '%s'.", fname); + pa_xfree(fname); + + for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx)) + subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u); + + for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx)) + subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u); + + pa_modargs_free(ma); + return 0; + +fail: + pa__done(m); + if (ma) + pa_modargs_free(ma); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata* u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + if (u->subscription) + pa_subscription_free(u->subscription); + + if (u->sink_fixate_hook_slot) + pa_hook_slot_free(u->sink_fixate_hook_slot); + if (u->source_fixate_hook_slot) + pa_hook_slot_free(u->source_fixate_hook_slot); + + if (u->save_time_event) + u->core->mainloop->time_free(u->save_time_event); + + if (u->gdbm_file) + gdbm_close(u->gdbm_file); + + pa_xfree(u); +} -- cgit From d491adff74640040b2ae3fba0bd864e1178ba461 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:23:06 +0000 Subject: add gccmacro.h to doxygen docs git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2237 fefdeb5f-60dc-0310-8127-8f9354f1896f --- doxygen/doxygen.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in index a45dc2db..7ad5d2f3 100644 --- a/doxygen/doxygen.conf.in +++ b/doxygen/doxygen.conf.in @@ -417,7 +417,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h +INPUT = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h ../src/pulse/gccmacro.h # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -- cgit From 007f82d351a1c40bbfce19c5299ea2d541d0b117 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:27:31 +0000 Subject: fix bad memory access when initializing client proplist git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2238 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pulse/context.c b/src/pulse/context.c index b9d93083..abc6a855 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -1205,8 +1205,10 @@ void pa_init_proplist(pa_proplist *p) { } } - if (!(a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY)) || - !(b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME))) { + a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY); + b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME); + + if (!a || !b) { char t[PATH_MAX]; if (pa_get_binary_name(t, sizeof(t))) { char *c = pa_utf8_filter(t); -- cgit From 566322ad30648317f9fbf2e5bd43f5862e68bc60 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:28:17 +0000 Subject: remove gcc macros from cdecl.h because we have them in gccmacro.h now git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2239 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/cdecl.h | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/pulse/cdecl.h b/src/pulse/cdecl.h index e1f23d25..922ad276 100644 --- a/src/pulse/cdecl.h +++ b/src/pulse/cdecl.h @@ -41,22 +41,4 @@ #endif -#ifndef PA_GCC_PURE -#ifdef __GNUCC__ -#define PA_GCC_PURE __attribute__ ((pure)) -#else -/** This function's return value depends only the arguments list and global state **/ -#define PA_GCC_PURE -#endif -#endif - -#ifndef PA_GCC_CONST -#ifdef __GNUCC__ -#define PA_GCC_CONST __attribute__ ((pure)) -#else -/** This function's return value depends only the arguments list (stricter version of PA_GCC_CONST) **/ -#define PA_GCC_CONST -#endif -#endif - #endif -- cgit From 919bd98dff2f4cd357fe89f9e4fbc202ee5dc409 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:28:49 +0000 Subject: add new API function pa_timeval_add() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2240 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/timeval.c | 18 ++++++++++++++++++ src/pulse/timeval.h | 6 +++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/pulse/timeval.c b/src/pulse/timeval.c index 70ceb71e..180e0159 100644 --- a/src/pulse/timeval.c +++ b/src/pulse/timeval.c @@ -148,6 +148,24 @@ struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) { return tv; } +struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v) { + unsigned long secs; + pa_assert(tv); + + secs = (unsigned long) (v/PA_USEC_PER_SEC); + tv->tv_sec -= secs; + v -= ((pa_usec_t) secs) * PA_USEC_PER_SEC; + + if (tv->tv_usec >= (suseconds_t) v) + tv->tv_usec -= (suseconds_t) v; + else { + tv->tv_sec --; + tv->tv_usec = tv->tv_usec + PA_USEC_PER_SEC - v; + } + + return tv; +} + struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v) { pa_assert(tv); diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 65a0e513..315f4190 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -26,6 +26,7 @@ ***/ #include +#include #include /** \file @@ -54,7 +55,10 @@ int pa_timeval_cmp(const struct timeval *a, const struct timeval *b) PA_GCC_PURE pa_usec_t pa_timeval_age(const struct timeval *tv); /** Add the specified time inmicroseconds to the specified timeval structure */ -struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v) PA_GCC_PURE; +struct timeval* pa_timeval_add(struct timeval *tv, pa_usec_t v); + +/** Subtract the specified time inmicroseconds to the specified timeval structure. \since 0.9.11 */ +struct timeval* pa_timeval_sub(struct timeval *tv, pa_usec_t v); /** Store the specified uec value in the timeval struct. \since 0.9.7 */ struct timeval* pa_timeval_store(struct timeval *tv, pa_usec_t v); -- cgit From 7dad6350e3a80e234278261ed41a83ea236b67b5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:29:22 +0000 Subject: fix bit depth guarantee for pa_usec_t git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2241 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/sample.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pulse/sample.h b/src/pulse/sample.h index f0b839fd..dedd72de 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -30,6 +30,7 @@ #include #include +#include #include /** \page sample Sample Format Specifications @@ -172,7 +173,7 @@ typedef struct pa_sample_spec { uint8_t channels; /**< Audio channels. (1 for mono, 2 for stereo, ...) */ } pa_sample_spec; -/** Type for usec specifications (unsigned). May be either 32 or 64 bit, depending on the architecture */ +/** Type for usec specifications (unsigned). Always 64 bit. */ typedef uint64_t pa_usec_t; /** Return the amount of bytes playback of a second of audio with the specified sample type takes */ -- cgit From e832b0c7fa3ae17adcbbea23183caeca718ac85a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:30:14 +0000 Subject: add C++ safety to header file git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2242 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/proplist.h | 83 ++++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 7c3423fa..302a59db 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -24,7 +24,10 @@ USA. ***/ -#include +#include +#include + +PA_C_DECL_BEGIN /* Defined properties: * @@ -65,43 +68,43 @@ * device.buffer_size */ -#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" -#define PA_PROP_DEVICE_BUFFER_NFRAGMENTS "device.buffer.nfragments" -#define PA_PROP_DEVICE_BUFFER_FRAGMENT_SIZE "device.buffer.fragment_size" +#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" +#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE "device.buffering.buffer_size" +#define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size" /** A property list object. Basically a dictionary with UTF-8 strings * as keys and arbitrary data as values. \since 0.9.11 */ @@ -176,7 +179,7 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]); * list do not have any particular order. \since 0.9.11 */ const char *pa_proplist_iterate(pa_proplist *p, void **state); -/** Format the property list nicely as a human readable string. \since +/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since * 0.9.11 */ char *pa_proplist_to_string(pa_proplist *p); @@ -191,4 +194,6 @@ void pa_proplist_clear(pa_proplist *p); * the specific list. \since 0.9.11 */ pa_proplist* pa_proplist_copy(pa_proplist *template); +PA_C_DECL_END + #endif -- cgit From d7e260bf6eb53c111c443c8c49d991651f7e7619 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:30:34 +0000 Subject: remove misplaced PA_GCC_PURE git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2243 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/volume.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 0fbf872f..245cc129 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -26,7 +26,9 @@ ***/ #include + #include +#include #include /** \page volume Volume Control @@ -149,7 +151,7 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; /** Multiply to per-channel volumes and return the result in *dest. This is only valid for software volumes! */ -pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) PA_GCC_PURE; +pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); /** Convert a decibel value to a volume. This is only valid for software volumes! */ pa_volume_t pa_sw_volume_from_dB(double f) PA_GCC_CONST; -- cgit From 0f28de6f17fd9416db21792cf2dbfdb51ef5b5b6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:31:01 +0000 Subject: mark autoload functions as deprecated git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2244 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/introspect.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 09cee608..d185a3a6 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -572,22 +573,22 @@ typedef struct pa_autoload_info { typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata); /** Get info about a specific autoload entry. */ -pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata); +pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED; /** Get info about a specific autoload entry. */ -pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata); +pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED; /** Get the complete list of autoload entries. */ -pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata); +pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED; /** Add a new autoload entry. */ -pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata); +pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED; /** Remove an autoload entry. */ -pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata); +pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED; /** Remove an autoload entry. */ -pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata); +pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED; /** @} */ -- cgit From 1c5f66519d06b65099c89e44a40c8ba070c0ca64 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:32:03 +0000 Subject: make use of new alsa SND_PCM_NO_AUTO_xxx flags; redirect alsa errors to normal PA log system git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2245 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.c | 44 ++++++++++++++++++++++++++++++++++++++++---- src/modules/alsa-util.h | 4 ++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index c8f594ac..9c9fc72f 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "alsa-util.h" @@ -571,7 +572,11 @@ snd_pcm_t *pa_alsa_open_by_device_id( d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id); pa_log_debug("Trying %s...", d); - if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK)) < 0) { + if ((err = snd_pcm_open(&pcm_handle, d, mode, + SND_PCM_NONBLOCK| + SND_PCM_NO_AUTO_RESAMPLE| + SND_PCM_NO_AUTO_CHANNELS| + SND_PCM_NO_AUTO_FORMAT)) < 0) { pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err)); pa_xfree(d); continue; @@ -595,9 +600,9 @@ snd_pcm_t *pa_alsa_open_by_device_id( return pcm_handle; } - /* OK, we didn't find any good device, so let's try the raw hw: stuff */ + /* OK, we didn't find any good device, so let's try the raw plughw: stuff */ - d = pa_sprintf_malloc("hw:%s", dev_id); + d = pa_sprintf_malloc("plughw:%s", dev_id); pa_log_debug("Trying %s as last resort...", d); pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched); pa_xfree(d); @@ -632,7 +637,10 @@ snd_pcm_t *pa_alsa_open_by_device_string( for (;;) { - if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK)) < 0) { + if ((err = snd_pcm_open(&pcm_handle, d, mode, SND_PCM_NONBLOCK| + SND_PCM_NO_AUTO_RESAMPLE| + SND_PCM_NO_AUTO_CHANNELS| + SND_PCM_NO_AUTO_FORMAT)) < 0) { pa_log("Error opening PCM device %s: %s", d, snd_strerror(err)); pa_xfree(d); return NULL; @@ -937,3 +945,31 @@ void pa_alsa_dump_status(snd_pcm_t *pcm) { pa_assert_se(snd_output_close(out) == 0); } + +static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) { + va_list ap; + + va_start(ap, fmt); + + pa_log_levelv_meta(PA_LOG_WARN, file, line, function, fmt, ap); + + va_end(ap); +} + +static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0); + +void pa_alsa_redirect_errors_inc(void) { + /* This is not really thread safe, but we do our best */ + + if (pa_atomic_inc(&n_error_handler_installed) == 0) + snd_lib_error_set_handler(alsa_error_handler); +} + +void pa_alsa_redirect_errors_dec(void) { + int r; + + pa_assert_se((r = pa_atomic_dec(&n_error_handler_installed)) >= 1); + + if (r == 1) + snd_lib_error_set_handler(NULL); +} diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 0e536f1b..51222a4f 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -85,4 +85,8 @@ void pa_alsa_0dB_capture(snd_mixer_elem_t *elem); void pa_alsa_dump(snd_pcm_t *pcm); void pa_alsa_dump_status(snd_pcm_t *pcm); +void pa_alsa_redirect_errors_inc(void); +void pa_alsa_redirect_errors_dec(void); + + #endif -- cgit From 78368db54f1e47dd4d7c74142d1a7abebb85f8bb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:33:33 +0000 Subject: redirect alsa errors to normal PA log system; export buffer settings in device props git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2246 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 29 +++++++++++++++++++++++------ src/modules/module-alsa-source.c | 11 +++++++++-- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 513350fc..f8a2c48b 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -60,7 +60,7 @@ PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "sink_name= " "device= " - "device_id= " + "device_id= " "format= " "rate= " "channels= " @@ -74,7 +74,7 @@ PA_MODULE_USAGE( #define DEFAULT_DEVICE "default" #define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) -#define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC) +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) struct userdata { pa_core *core; @@ -267,6 +267,7 @@ static int unix_write(struct userdata *u) { int err; snd_pcm_hwsync(u->pcm_handle); + snd_pcm_avail_update(u->pcm_handle); if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { pa_log("Failed to query DSP status data: %s", snd_strerror(err)); @@ -343,6 +344,10 @@ static void update_smoother(struct userdata *u) { int64_t frames; int err; pa_usec_t now1, now2; +/* struct timeval timestamp; */ + snd_pcm_status_t *status; + + snd_pcm_status_alloca(&status); pa_assert(u); pa_assert(u->pcm_handle); @@ -352,15 +357,19 @@ static void update_smoother(struct userdata *u) { snd_pcm_hwsync(u->pcm_handle); snd_pcm_avail_update(u->pcm_handle); - if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { - pa_log_warn("Failed to get delay: %s", snd_strerror(err)); + if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { + pa_log("Failed to query DSP status data: %s", snd_strerror(err)); return; } + delay = snd_pcm_status_get_delay(status); frames = u->frame_index - delay; - pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); +/* snd_pcm_status_get_tstamp(status, ×tamp); */ +/* pa_rtclock_from_wallclock(×tamp); */ +/* now1 = pa_timeval_load(×tamp); */ + now1 = pa_rtclock_usec(); now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec); pa_smoother_put(u->smoother, now1, now2); @@ -1008,6 +1017,8 @@ int pa__init(pa_module*m) { pa_assert(m); + pa_alsa_redirect_errors_inc(); + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; @@ -1185,6 +1196,8 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "alsa"); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, snd_pcm_info_get_name(pcm_info)); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); if (class_table[snd_pcm_info_get_class(pcm_info)]) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, class_table[snd_pcm_info_get_class(pcm_info)]); @@ -1366,8 +1379,10 @@ void pa__done(pa_module*m) { pa_assert(m); - if (!(u = m->userdata)) + if (!(u = m->userdata)) { + pa_alsa_redirect_errors_dec(); return; + } if (u->sink) pa_sink_unlink(u->sink); @@ -1409,4 +1424,6 @@ void pa__done(pa_module*m) { pa_xfree(u); snd_config_update_free_global(); + + pa_alsa_redirect_errors_dec(); } diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index caaa5458..83b7f53a 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -61,7 +61,7 @@ PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "source_name= " "device= " - "device_id= " + "device_id= " "format= " "rate= " "channels= " @@ -889,6 +889,8 @@ int pa__init(pa_module*m) { pa_assert(m); + pa_alsa_redirect_errors_inc(); + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; @@ -1061,6 +1063,8 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "alsa"); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, snd_pcm_info_get_name(pcm_info)); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); if (class_table[snd_pcm_info_get_class(pcm_info)]) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, class_table[snd_pcm_info_get_class(pcm_info)]); @@ -1228,8 +1232,10 @@ void pa__done(pa_module*m) { pa_assert(m); - if (!(u = m->userdata)) + if (!(u = m->userdata)) { + pa_alsa_redirect_errors_dec(); return; + } if (u->source) pa_source_unlink(u->source); @@ -1268,4 +1274,5 @@ void pa__done(pa_module*m) { pa_xfree(u); snd_config_update_free_global(); + pa_alsa_redirect_errors_dec(); } -- cgit From d0ebb71eede6c626e61aac27aabbe679638358f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:34:11 +0000 Subject: don't use fqdn if we don't have to git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2247 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-http.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c index d91ae142..589eba4f 100644 --- a/src/pulsecore/protocol-http.c +++ b/src/pulsecore/protocol-http.c @@ -168,7 +168,7 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) { #define PRINTF_FIELD(a,b) pa_ioline_printf(c->line, "%s%s\n",(a),(b)) PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt))); - PRINTF_FIELD("Fully Qualified Domain Name:", pa_get_fqdn(txt, sizeof(txt))); + PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt))); PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec)); PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core)); PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core)); -- cgit From 50d585e458039bf428d76deb04cd378e61d4b533 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:35:31 +0000 Subject: fix linker warning macro code, move pa_strnull() to core-util.h, move PA_LIKELY definitions here from gccmacro.h git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2248 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/macro.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h index a301bba0..1d9eafd5 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -33,12 +33,22 @@ #include #include -#include +#include #ifndef PACKAGE #error "Please include config.h before including this file!" #endif +#ifndef PA_LIKELY +#ifdef __GNUC__ +#define PA_LIKELY(x) (__builtin_expect(!!(x),1)) +#define PA_UNLIKELY(x) (__builtin_expect((x),0)) +#else +#define PA_LIKELY(x) (x) +#define PA_UNLIKELY(x) (x) +#endif +#endif + #if defined(PAGE_SIZE) #define PA_PAGE_SIZE ((size_t) PAGE_SIZE) #elif defined(PAGESIZE) @@ -200,20 +210,16 @@ typedef int pa_bool_t; #define PA_PATH_SEP_CHAR '/' #endif -static inline const char *pa_strnull(const char *x) { - return x ? x : "(null)"; -} - #ifdef __GNUC__ -#define PA_WARN_REFERENCE(sym,msg) \ - __asm__(".section .gnu.warning.sym"); \ - __asm__(".asciz \"msg\""); \ +#define PA_WARN_REFERENCE(sym, msg) \ + __asm__(".section .gnu.warning." #sym); \ + __asm__(".asciz \"" msg "\""); \ __asm__(".previous") #else -#define PA_WARN_REFERENCE(sym,msg) +#define PA_WARN_REFERENCE(sym, msg) #endif -- cgit From 28ab2a0c1dc9e4cf86d8b583748796754b607096 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:36:13 +0000 Subject: don't print 'signal' each time a rtpoll poll() call is canceled git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2249 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/rtpoll.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index 74035081..afcd7e5d 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -98,7 +98,7 @@ struct pa_rtpoll_item { PA_STATIC_FLIST_DECLARE(items, 0, pa_xfree); -static void signal_handler_noop(int s) { write(2, "signal\n", 7); } +static void signal_handler_noop(int s) { /* write(2, "signal\n", 7); */ } pa_rtpoll *pa_rtpoll_new(void) { pa_rtpoll *p; -- cgit From 413656bce6ed85e4732412ddf89e93fc7a46683a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:36:38 +0000 Subject: update list-xxx commands a bit git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2250 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/cli-text.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 5bf27eec..140334a1 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -57,12 +57,12 @@ char *pa_module_list_to_string(pa_core *c) { for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) { pa_strbuf_printf(s, " index: %u\n" - "\tname: <%s>\n" - "\targument: <%s>\n" - "\tused: %i\n" - "\tauto unload: %s\n", - m->index, m->name, m->argument ? m->argument : "", m->n_used, - m->auto_unload ? "yes" : "no"); + "\tname: <%s>\n" + "\targument: <%s>\n" + "\tused: %i\n" + "\tauto unload: %s\n", + m->index, m->name, m->argument ? m->argument : "", m->n_used, + pa_yes_no(m->auto_unload)); } return pa_strbuf_tostring_free(s); @@ -138,12 +138,12 @@ char *pa_sink_list_to_string(pa_core *c) { sink->index, sink->name, sink->driver, + sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "", + sink->flags & PA_SINK_NETWORK ? "NETWORK " : "", sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "", sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", sink->flags & PA_SINK_LATENCY ? "LATENCY " : "", - sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "", - sink->flags & PA_SINK_NETWORK ? "NETWORK " : "", state_table[pa_sink_get_state(sink)], pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)), !!pa_sink_get_mute(sink), @@ -205,12 +205,12 @@ char *pa_source_list_to_string(pa_core *c) { source->index, source->name, source->driver, + source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "", + source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "", source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "", source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", - source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "", - source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "", state_table[pa_source_get_state(source)], pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)), !!pa_source_get_mute(source), -- cgit From c9db6d2543fd0be51a6c73ef3afb1462c1924f1b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:37:22 +0000 Subject: don't fail if a signalled writability of STDOUT is no longer true when we try it because some other thread already wrote something git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2251 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/ioline.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c index 5fd2189b..860a6511 100644 --- a/src/pulsecore/ioline.c +++ b/src/pulsecore/ioline.c @@ -299,6 +299,10 @@ static int do_read(pa_ioline *l) { /* Read some data */ if ((r = pa_iochannel_read(l->io, l->rbuf+l->rbuf_index+l->rbuf_valid_length, len)) <= 0) { + + if (r < 0 && errno == EAGAIN) + return 0; + if (r < 0 && errno != ECONNRESET) { pa_log("read(): %s", pa_cstrerror(errno)); failure(l, 0); @@ -328,6 +332,9 @@ static int do_write(pa_ioline *l) { if ((r = pa_iochannel_write(l->io, l->wbuf+l->wbuf_index, l->wbuf_valid_length)) <= 0) { + if (r < 0 && errno == EAGAIN) + return 0; + if (r < 0 && errno != EPIPE) pa_log("write(): %s", pa_cstrerror(errno)); -- cgit From 096e7f0f817d5dbf130e623bdae0388019bfbfc6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:38:16 +0000 Subject: make shm magic marker compat with multiarch systems where 64bit and 32bit processes might share SHM areas git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2252 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/shm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index 59341f04..33034e24 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -73,10 +73,10 @@ struct shm_marker { pa_atomic_t marker; /* 0xbeefcafe */ pa_atomic_t pid; - void *_reserverd1; - void *_reserverd2; - void *_reserverd3; - void *_reserverd4; + uint64_t *_reserverd1; + uint64_t *_reserverd2; + uint64_t *_reserverd3; + uint64_t *_reserverd4; }; static char *segment_name(char *fn, size_t l, unsigned id) { -- cgit From 29cbd88138291d4344bad166b9943814b943e293 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:51:21 +0000 Subject: add new PA_GCC_PACKED macro git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2253 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/gccmacro.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h index 5a2a250f..032c3bae 100644 --- a/src/pulse/gccmacro.h +++ b/src/pulse/gccmacro.h @@ -86,4 +86,13 @@ #endif #endif +#ifndef PA_GCC_PACKED +#ifdef __GNUCC__ +#define PA_GCC_PACKED __attribute__ ((packed)) +#else +/** Structure shall be packed in memory **/ +#define PA_GCC_PACKED +#endif +#endif + #endif -- cgit From 2c6176fdd40ccec36b3788a07289cf57cafd028f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:52:45 +0000 Subject: mark shm marker struct as packed, to guarantee identical sizes between archs git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2254 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/shm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index 33034e24..a1bbf609 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -42,6 +42,7 @@ #endif #include +#include #include #include @@ -69,8 +70,9 @@ #define SHM_MARKER ((int) 0xbeefcafe) -/* We now put this SHM marker at the end of each segment. It's optional to not require a reboot when upgrading, though */ -struct shm_marker { +/* We now put this SHM marker at the end of each segment. It's + * optional, to not require a reboot when upgrading, though */ +struct shm_marker PA_GCC_PACKED { pa_atomic_t marker; /* 0xbeefcafe */ pa_atomic_t pid; uint64_t *_reserverd1; -- cgit From fe3c42dbfe1a80a4112df61a25dfa285005356c2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:53:29 +0000 Subject: fix packet formatting for a few commands git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2255 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-native.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index db52b7f8..fc5aa598 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -2186,8 +2186,10 @@ static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) { PA_TAG_U32, sink->flags, PA_TAG_INVALID); - if (c->version >= 13) + if (c->version >= 13) { pa_tagstruct_put_proplist(t, sink->proplist); + pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink)); + } } static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *source) { @@ -2215,10 +2217,13 @@ static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *sou PA_TAG_U32, source->flags, PA_TAG_INVALID); - if (c->version >= 13) + if (c->version >= 13) { pa_tagstruct_put_proplist(t, source->proplist); + pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source)); + } } + static void client_fill_tagstruct(connection *c, pa_tagstruct *t, pa_client *client) { pa_assert(t); pa_assert(client); @@ -2266,10 +2271,8 @@ static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_in pa_tagstruct_puts(t, s->driver); if (c->version >= 11) pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s)); - if (c->version >= 13) { + if (c->version >= 13) pa_tagstruct_put_proplist(t, s->proplist); - pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(s->sink)); - } } static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) { @@ -2292,10 +2295,8 @@ static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sour pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s))); pa_tagstruct_puts(t, s->driver); - if (c->version >= 13) { + if (c->version >= 13) pa_tagstruct_put_proplist(t, s->proplist); - pa_tagstruct_put_usec(t, pa_source_get_requested_latency(s->source)); - } } static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entry *e) { @@ -2480,7 +2481,7 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE pa_tagstruct_puts(reply, PACKAGE_NAME); pa_tagstruct_puts(reply, PACKAGE_VERSION); pa_tagstruct_puts(reply, pa_get_user_name(txt, sizeof(txt))); - pa_tagstruct_puts(reply, pa_get_fqdn(txt, sizeof(txt))); + pa_tagstruct_puts(reply, pa_get_host_name(txt, sizeof(txt))); fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec); pa_tagstruct_put_sample_spec(reply, &fixed_ss); -- cgit From dcf71734892ed6f8076a6ae37718fa02c750be7a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:57:26 +0000 Subject: fix help string for volume commands git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2256 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/cli-command.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index ced56c24..7c85224e 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -477,7 +477,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu } if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n"); return -1; } @@ -519,7 +519,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb } if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n"); return -1; } @@ -555,7 +555,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf * } if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n"); return -1; } @@ -664,7 +664,7 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf } if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x100 is normal volume)\n"); + pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n"); return -1; } -- cgit From cdb273de904a0a66fee2c0dc0c164455f8c035ac Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 16:58:07 +0000 Subject: add new pa_get_state_dir() function, move pa_strnull() here git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2257 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/core-util.c | 29 +++++++++++++++++++++++++++++ src/pulsecore/core-util.h | 8 +++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 61d04c2d..28885b2c 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -1093,6 +1093,35 @@ int pa_unlock_lockfile(const char *fn, int fd) { return r; } +char *pa_get_state_dir(void) { + const char *e; + char *d; + + if ((e = getenv("PULSE_STATE_PATH"))) + d = pa_xstrdup(e); + else { + char h[PATH_MAX]; + + if (!pa_get_home_dir(h, sizeof(h))) { + pa_log_error("Failed to get home directory."); + return NULL; + } + + d = pa_sprintf_malloc("%s/.pulse", h); + } + + mkdir(d, 0755); + + if (access(d, W_OK) == 0) + return d; + + pa_log_error("Failed to set up state directory %s", d); + + pa_xfree(d); + + return NULL; +} + /* Try to open a configuration file. If "env" is specified, open the * value of the specified environment variable. Otherwise look for a * file "local" in the home directory or a file "global" in global diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index c8760a1f..d5c0a3f6 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -30,7 +30,7 @@ #include #include -#include +#include #include struct timeval; @@ -67,6 +67,10 @@ static inline const char *pa_yes_no(pa_bool_t b) { return b ? "yes" : "no"; } +static inline const char *pa_strnull(const char *x) { + return x ? x : "(null)"; +} + char *pa_split(const char *c, const char*delimiters, const char **state); char *pa_split_spaces(const char *c, const char **state); @@ -129,4 +133,6 @@ void pa_close_pipe(int fds[2]); char *pa_readlink(const char *p); +char *pa_get_state_dir(void); + #endif -- cgit From dbe36333f37ad76a9ba011211740b79da7e4683c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 17:02:42 +0000 Subject: properly initialize ->memblockq git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2258 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sound-file-stream.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 60c2560e..5c8400da 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -247,6 +247,7 @@ int pa_play_file( u->sink_input = NULL; u->sndfile = NULL; u->readf_function = NULL; + u->memblockq = NULL; memset(&sfinfo, 0, sizeof(sfinfo)); -- cgit From aad9d39838d247e563354e56ccdea280a428d098 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 17:03:18 +0000 Subject: dump all info we know about sinks/sources/... in pactl git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2259 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/utils/pactl.c | 105 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 674eaee6..1f875a88 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -158,6 +158,7 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) { char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char *pl; if (is_last < 0) { fprintf(stderr, "Failed to get sink information: %s\n", pa_strerror(pa_context_errno(c))); @@ -179,32 +180,37 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_ printf("*** Sink #%u ***\n" "Name: %s\n" "Driver: %s\n" - "Description: %s\n" "Sample Specification: %s\n" "Channel Map: %s\n" "Owner Module: %u\n" "Volume: %s\n" - "Monitor Source: %u\n" - "Latency: %0.0f usec\n" - "Flags: %s%s%s\n", + "Monitor Source: %s\n" + "Latency: %0.0f usec, configured %0.0f usec\n" + "Flags: %s%s%s%s%s%s\n" + "Properties:\n%s", i->index, i->name, - i->driver, - i->description, + pa_strnull(i->driver), pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), i->owner_module, i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume), - i->monitor_source, - (double) i->latency, + pa_strnull(i->monitor_source_name), + (double) i->latency, (double) i->configured_latency, + i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "", + i->flags & PA_SINK_NETWORK ? "NETWORK " : "", + i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "", i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", + i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", i->flags & PA_SINK_LATENCY ? "LATENCY " : "", - i->flags & PA_SINK_HARDWARE ? "HARDWARE" : ""); + pl = pa_proplist_to_string(i->proplist)); + pa_xfree(pl); } static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) { - char s[PA_SAMPLE_SPEC_SNPRINT_MAX], t[32], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char *pl; if (is_last < 0) { fprintf(stderr, "Failed to get source information: %s\n", pa_strerror(pa_context_errno(c))); @@ -223,33 +229,35 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int printf("\n"); nl = 1; - snprintf(t, sizeof(t), "%u", i->monitor_of_sink); - printf("*** Source #%u ***\n" "Name: %s\n" "Driver: %s\n" - "Description: %s\n" "Sample Specification: %s\n" "Channel Map: %s\n" "Owner Module: %u\n" "Volume: %s\n" "Monitor of Sink: %s\n" - "Latency: %0.0f usec\n" - "Flags: %s%s%s\n", + "Latency: %0.0f usec, configured %0.0f usec\n" + "Flags: %s%s%s%s%s%s\n" + "Properties:\n%s", i->index, i->name, - i->driver, - i->description, + pa_strnull(i->driver), pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), i->owner_module, i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume), - i->monitor_of_sink != PA_INVALID_INDEX ? t : "no", - (double) i->latency, + i->monitor_of_sink_name ? i->monitor_of_sink_name : "n/a", + (double) i->latency, (double) i->configured_latency, + i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "", + i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "", + i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "", i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", + i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", - i->flags & PA_SOURCE_HARDWARE ? "HARDWARE" : ""); + pl = pa_proplist_to_string(i->proplist)); + pa_xfree(pl); } static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) { @@ -283,11 +291,12 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int i->name, i->argument ? i->argument : "", i->n_used != PA_INVALID_INDEX ? t : "n/a", - i->auto_unload ? "yes" : "no"); + pa_yes_no(i->auto_unload)); } static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) { char t[32]; + char *pl; if (is_last < 0) { fprintf(stderr, "Failed to get client information: %s\n", pa_strerror(pa_context_errno(c))); @@ -309,17 +318,20 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int snprintf(t, sizeof(t), "%u", i->owner_module); printf("*** Client #%u ***\n" - "Name: %s\n" "Driver: %s\n" - "Owner Module: %s\n", + "Owner Module: %s\n" + "Properties:\n%s", i->index, - i->name, - i->driver, - i->owner_module != PA_INVALID_INDEX ? t : "n/a"); + pa_strnull(i->driver), + i->owner_module != PA_INVALID_INDEX ? t : "n/a", + pl = pa_proplist_to_string(i->proplist)); + + pa_xfree(pl); } static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) { char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char *pl; if (is_last < 0) { fprintf(stderr, "Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c))); @@ -342,7 +354,6 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info snprintf(k, sizeof(k), "%u", i->client); printf("*** Sink Input #%u ***\n" - "Name: %s\n" "Driver: %s\n" "Owner Module: %s\n" "Client: %s\n" @@ -352,10 +363,10 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info "Volume: %s\n" "Buffer Latency: %0.0f usec\n" "Sink Latency: %0.0f usec\n" - "Resample method: %s\n", + "Resample method: %s\n" + "Properties:\n%s", i->index, - i->name, - i->driver, + pa_strnull(i->driver), i->owner_module != PA_INVALID_INDEX ? t : "n/a", i->client != PA_INVALID_INDEX ? k : "n/a", i->sink, @@ -364,12 +375,15 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume), (double) i->buffer_usec, (double) i->sink_usec, - i->resample_method ? i->resample_method : "n/a"); -} + i->resample_method ? i->resample_method : "n/a", + pl = pa_proplist_to_string(i->proplist)); + pa_xfree(pl); +} static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) { char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char *pl; if (is_last < 0) { fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c))); @@ -393,7 +407,6 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu snprintf(k, sizeof(k), "%u", i->client); printf("*** Source Output #%u ***\n" - "Name: %s\n" "Driver: %s\n" "Owner Module: %s\n" "Client: %s\n" @@ -402,10 +415,10 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu "Channel Map: %s\n" "Buffer Latency: %0.0f usec\n" "Source Latency: %0.0f usec\n" - "Resample method: %s\n", + "Resample method: %s\n" + "Properties:\n%s", i->index, - i->name, - i->driver, + pa_strnull(i->driver), i->owner_module != PA_INVALID_INDEX ? t : "n/a", i->client != PA_INVALID_INDEX ? k : "n/a", i->source, @@ -413,11 +426,15 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), (double) i->buffer_usec, (double) i->source_usec, - i->resample_method ? i->resample_method : "n/a"); + i->resample_method ? i->resample_method : "n/a", + pl = pa_proplist_to_string(i->proplist)); + + pa_xfree(pl); } static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) { char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char *pl; if (is_last < 0) { fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c))); @@ -447,7 +464,8 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int "Duration: %0.1fs\n" "Size: %s\n" "Lazy: %s\n" - "Filename: %s\n", + "Filename: %s\n" + "Properties:\n%s", i->index, i->name, pa_cvolume_snprint(cv, sizeof(cv), &i->volume), @@ -455,8 +473,11 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : "n/a", (double) i->duration/1000000, t, - i->lazy ? "yes" : "no", - i->filename ? i->filename : "n/a"); + pa_yes_no(i->lazy), + i->filename ? i->filename : "n/a", + pl = pa_proplist_to_string(i->proplist)); + + pa_xfree(pl); } static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) { @@ -868,6 +889,10 @@ int main(int argc, char *argv[]) { if (argc > optind+2) source_name = pa_xstrdup(argv[optind+1]); + } else if (!strcmp(argv[optind], "help")) { + help(bn); + ret = 0; + goto quit; } } -- cgit From 07f5c1d8e433b5feb4f8b7a4307477e071234f1a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 17:05:46 +0000 Subject: register sink/source name as first step when creating a new sink/source so that we can hand the valid name string to the hook functions; se tup props for monitor sources correctly; fix implicit flag setting logic git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2260 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 39 +++++++++++++++++++++------------------ src/pulsecore/source.c | 37 +++++++++++++++---------------------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 02f568e3..7c1256f6 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -118,9 +118,22 @@ pa_sink* pa_sink_new( pa_assert(core); pa_assert(data); + pa_assert(data->name); - if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) + s = pa_msgobject_new(pa_sink); + + if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) { + pa_xfree(s); + return NULL; + } + + pa_sink_new_data_set_name(data, name); + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_NEW], data) < 0) { + pa_xfree(s); + pa_namereg_unregister(core, name); return NULL; + } pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); @@ -142,13 +155,9 @@ pa_sink* pa_sink_new( if (!data->muted_is_set) data->muted = FALSE; - if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) - return NULL; - - s = pa_msgobject_new(pa_sink); - - if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SINK, s, data->namereg_fail))) { + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) { pa_xfree(s); + pa_namereg_unregister(core, name); return NULL; } @@ -212,11 +221,12 @@ pa_sink* pa_sink_new( pa_source_new_data_set_channel_map(&source_data, &s->channel_map); source_data.name = pa_sprintf_malloc("%s.monitor", name); source_data.driver = data->driver; + source_data.module = data->module; d = pa_sprintf_malloc("Monitor Source of %s", s->name); - pa_proplist_sets(data->proplist, PA_PROP_DEVICE_DESCRIPTION, d); + pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, d); pa_xfree(d); - pa_proplist_sets(data->proplist, PA_PROP_DEVICE_CLASS, "monitor"); + pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor"); s->monitor_source = pa_source_new(core, &source_data, 0); @@ -281,15 +291,8 @@ void pa_sink_put(pa_sink* s) { pa_assert(!s->min_latency || !s->max_latency || s->min_latency <= s->max_latency); - if (s->get_volume && s->set_volume) - s->flags |= PA_SINK_HW_VOLUME_CTRL; - else - s->flags = (s->flags & ~PA_SINK_HW_VOLUME_CTRL) | PA_SINK_DECIBEL_VOLUME; - - if (s->get_mute && s->set_mute) - s->flags |= PA_SINK_HW_MUTE_CTRL; - else - s->flags &= ~PA_SINK_HW_MUTE_CTRL; + if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) + s->flags |= PA_SINK_DECIBEL_VOLUME; pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 5a4b630b..4f963325 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -110,8 +110,18 @@ pa_source* pa_source_new( pa_assert(core); - if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) + s = pa_msgobject_new(pa_source); + + if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) { + pa_xfree(s); return NULL; + } + + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_NEW], data) < 0) { + pa_xfree(s); + pa_namereg_unregister(core, name); + return NULL; + } pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver)); pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]); @@ -133,13 +143,9 @@ pa_source* pa_source_new( if (!data->muted_is_set) data->muted = FALSE; - if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) - return NULL; - - s = pa_msgobject_new(pa_source); - - if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_SOURCE, s, data->namereg_fail))) { + if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) { pa_xfree(s); + pa_namereg_unregister(core, name); return NULL; } @@ -246,21 +252,8 @@ void pa_source_put(pa_source *s) { pa_assert(!s->min_latency || !s->max_latency || s->min_latency <= s->max_latency); - if (s->get_volume && s->set_volume) - s->flags |= PA_SOURCE_HW_VOLUME_CTRL; - else { - s->get_volume = NULL; - s->set_volume = NULL; - s->flags = (s->flags & ~PA_SOURCE_HW_VOLUME_CTRL) | PA_SOURCE_DECIBEL_VOLUME; - } - - if (s->get_mute && s->set_mute) - s->flags |= PA_SOURCE_HW_MUTE_CTRL; - else { - s->get_mute = NULL; - s->set_mute = NULL; - s->flags &= ~PA_SOURCE_HW_MUTE_CTRL; - } + if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) + s->flags |= PA_SOURCE_DECIBEL_VOLUME; pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); -- cgit From 55f273eb008cac7c2307057aa0ce85cf297b982c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 17:06:41 +0000 Subject: s/pulsecore\/gccmacro.h/pulse\/gccmacro.h/ git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2261 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/channelmap.h | 1 + src/pulse/introspect.c | 2 +- src/pulse/mainloop-api.c | 3 +-- src/pulse/mainloop-signal.c | 2 +- src/pulse/subscribe.c | 3 ++- src/pulse/utf8.h | 1 + src/pulse/util.h | 1 + src/pulse/xmalloc.c | 2 +- src/pulsecore/client.c | 1 + src/pulsecore/flist.h | 2 +- src/pulsecore/hook-list.h | 5 +++-- src/pulsecore/log.h | 2 +- src/pulsecore/play-memblockq.c | 2 +- src/pulsecore/play-memchunk.c | 2 +- src/pulsecore/sink-input.c | 1 + src/pulsecore/source-output.c | 1 + src/pulsecore/strbuf.h | 2 +- src/pulsecore/tokenizer.c | 2 +- src/utils/padsp.c | 2 +- 19 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index 4122a318..00d3eb0d 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -27,6 +27,7 @@ #include #include +#include /** \page channelmap Channel Maps * diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 9c50b57b..49f93463 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -27,8 +27,8 @@ #endif #include +#include -#include #include #include diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c index b2ed3434..dda51297 100644 --- a/src/pulse/mainloop-api.c +++ b/src/pulse/mainloop-api.c @@ -28,8 +28,8 @@ #include #include +#include -#include #include #include "mainloop-api.h" @@ -75,4 +75,3 @@ void pa_mainloop_api_once(pa_mainloop_api* m, void (*callback)(pa_mainloop_api * pa_assert_se(e = m->defer_new(m, once_callback, i)); m->defer_set_destroy(e, free_callback); } - diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index e41ed14c..8ad465bd 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -39,11 +39,11 @@ #endif #include +#include #include #include #include -#include #include #include "mainloop-signal.h" diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index 580038cc..db25f3cf 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -27,7 +27,8 @@ #include -#include +#include + #include #include diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index 1e08047c..840c74e0 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -26,6 +26,7 @@ ***/ #include +#include /** \file * UTF8 Validation functions diff --git a/src/pulse/util.h b/src/pulse/util.h index 764678e5..666ccce4 100644 --- a/src/pulse/util.h +++ b/src/pulse/util.h @@ -28,6 +28,7 @@ #include #include +#include /** \file * Assorted utility functions */ diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index 5348dda4..28490975 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -30,8 +30,8 @@ #include #include +#include #include -#include #include #include "xmalloc.h" diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c index 6a087d46..4eca4e2a 100644 --- a/src/pulsecore/client.c +++ b/src/pulsecore/client.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "client.h" diff --git a/src/pulsecore/flist.h b/src/pulsecore/flist.h index daf0fec4..3d9a89a2 100644 --- a/src/pulsecore/flist.h +++ b/src/pulsecore/flist.h @@ -25,9 +25,9 @@ ***/ #include +#include #include -#include /* A multiple-reader multipler-write lock-free free list implementation */ diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h index b3bd600a..c288980d 100644 --- a/src/pulsecore/hook-list.h +++ b/src/pulsecore/hook-list.h @@ -24,9 +24,10 @@ USA. ***/ -#include #include -#include +#include + +#include typedef struct pa_hook_slot pa_hook_slot; typedef struct pa_hook pa_hook; diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h index b0711dca..765dd2e5 100644 --- a/src/pulsecore/log.h +++ b/src/pulsecore/log.h @@ -27,7 +27,7 @@ #include #include -#include +#include /* A simple logging subsystem */ diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index bad60ae5..3d435c8b 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -30,9 +30,9 @@ #include #include +#include #include -#include #include #include diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c index c528b1d0..e4d24a99 100644 --- a/src/pulsecore/play-memchunk.c +++ b/src/pulsecore/play-memchunk.c @@ -30,9 +30,9 @@ #include #include +#include #include -#include #include #include diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index ddd9980f..2d0ba449 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "sink-input.h" diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index cf576ac9..c0cbb42f 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "source-output.h" diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h index 1c0850b1..d3555a2c 100644 --- a/src/pulsecore/strbuf.h +++ b/src/pulsecore/strbuf.h @@ -24,7 +24,7 @@ USA. ***/ -#include +#include typedef struct pa_strbuf pa_strbuf; diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c index f79c19c5..cf5da648 100644 --- a/src/pulsecore/tokenizer.c +++ b/src/pulsecore/tokenizer.c @@ -29,9 +29,9 @@ #include #include +#include #include -#include #include #include "tokenizer.h" diff --git a/src/utils/padsp.c b/src/utils/padsp.c index d3f034d0..6ab022ee 100644 --- a/src/utils/padsp.c +++ b/src/utils/padsp.c @@ -53,8 +53,8 @@ #endif #include +#include #include -#include /* On some systems SIOCINQ isn't defined, but FIONREAD is just an alias */ #if !defined(SIOCINQ) && defined(FIONREAD) -- cgit From 14fd32ee4b20d9c3e50896f52c7e7b22e72db868 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Apr 2008 17:39:38 +0000 Subject: add missing 'break's in switch git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2262 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/tagstruct.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c index fb412a4a..7616cd16 100644 --- a/src/pulsecore/tagstruct.c +++ b/src/pulsecore/tagstruct.c @@ -667,6 +667,7 @@ void pa_tagstruct_put(pa_tagstruct *t, ...) { case PA_TAG_PROPLIST: pa_tagstruct_put_proplist(t, va_arg(va, pa_proplist *)); + break; default: pa_assert_not_reached(); @@ -741,6 +742,7 @@ int pa_tagstruct_get(pa_tagstruct *t, ...) { case PA_TAG_PROPLIST: ret = pa_tagstruct_get_proplist(t, va_arg(va, pa_proplist *)); + break; default: pa_assert_not_reached(); -- cgit From 04178d428e5f687849e00d8a38ba206003fdcfed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 14 Apr 2008 18:43:11 +0000 Subject: add _cb suffix to _max_rewind function like with all other functions, too git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2264 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sound-file-stream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 5c8400da..7e0f8ef1 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -212,7 +212,7 @@ static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(u->memblockq, nbytes); } -static void sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes) { +static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes) { file_stream *u; pa_sink_input_assert_ref(i); @@ -332,7 +332,7 @@ int pa_play_file( u->sink_input->pop = sink_input_pop_cb; u->sink_input->rewind = sink_input_rewind_cb; - u->sink_input->set_max_rewind = sink_input_set_max_rewind; + u->sink_input->set_max_rewind = sink_input_set_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->userdata = u; -- cgit From 6946d2ad2d905870bcbf98defc22c34ab6f21bd6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 14 Apr 2008 18:43:59 +0000 Subject: make sure to clear all queued RT signals before arm a new timer git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2265 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/rtpoll.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index afcd7e5d..bd1c43ad 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -462,9 +462,22 @@ static void update_timer(pa_rtpoll *p) { if (p->timer != (timer_t) -1) { struct itimerspec its; + struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; + sigset_t ss; + + /* First disarm timer */ memset(&its, 0, sizeof(its)); + pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); + + /* Remove a signal that might be waiting in the signal q */ + pa_assert_se(sigemptyset(&ss) == 0); + pa_assert_se(sigaddset(&ss, p->rtsig) == 0); + sigtimedwait(&ss, NULL, &ts); + /* And install the new timer */ if (p->timer_enabled) { + memset(&its, 0, sizeof(its)); + its.it_value.tv_sec = p->next_elapse.tv_sec; its.it_value.tv_nsec = p->next_elapse.tv_usec*1000; @@ -472,9 +485,8 @@ static void update_timer(pa_rtpoll *p) { * "disarming" */ if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0) its.it_value.tv_nsec = 1; + pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); } - - pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); } #ifdef __linux__ -- cgit From 68e4a93705117e14e28fa919873a239e907c73d8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 14 Apr 2008 18:45:08 +0000 Subject: properly ask the sink to rewind on new sink inputs and when they disappear git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2266 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 7c1256f6..6dcc7ab8 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -973,8 +973,8 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_invalidate_requested_latency(s); -/* i->thread_info.ignore_rewind = TRUE; */ -/* pa_sink_request_rewind(s, 0); */ + i->thread_info.ignore_rewind = TRUE; + pa_sink_request_rewind(s, 0); return 0; } @@ -1014,7 +1014,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_invalidate_requested_latency(s); -/* pa_sink_request_rewind(s, 0); */ + pa_sink_request_rewind(s, 0); return 0; } -- cgit From 1f0a52dad1515e4aa7c9f45541faaa03a88aa13a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 14 Apr 2008 18:46:24 +0000 Subject: the pointer to rewind() may actually be NULL git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2267 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink-input.c | 5 ++--- src/pulsecore/sink-input.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 2d0ba449..01e3bd9c 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -399,7 +399,6 @@ void pa_sink_input_put(pa_sink_input *i) { pa_assert(i->state == PA_SINK_INPUT_INIT); pa_assert(i->pop); - pa_assert(i->rewind); i->thread_info.state = i->state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; i->thread_info.volume = i->volume; @@ -611,7 +610,7 @@ void pa_sink_input_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); - pa_log_debug("rewind(%u, %u)", nbytes, i->thread_info.rewrite_nbytes); + pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); if (i->thread_info.state == PA_SINK_INPUT_CORKED) return; @@ -623,7 +622,7 @@ void pa_sink_input_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec } if (nbytes > 0) - pa_log_debug("Have to rewind %u bytes.", nbytes); + pa_log_debug("Have to rewind %lu bytes.", (unsigned long) nbytes); if (i->thread_info.rewrite_nbytes > 0) { size_t max_rewrite; diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 62464ca2..8545dea3 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -103,7 +103,7 @@ struct pa_sink_input { * before peek() if it is called at all. Only called if the sink * input driver ever plans to call * pa_sink_input_request_rewrite(). Called from IO context. */ - void (*rewind) (pa_sink_input *i, size_t nbytes); + void (*rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ /* Called whenever the maximum rewindable size of the sink * changes. Called from UI context. */ -- cgit From c9d01592d4aa57f955456a675ebb2281ac5850e1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 17 Apr 2008 16:24:26 +0000 Subject: define PA_xxxSEC_PER_yyySEC for usec, too git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2268 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/timeval.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h index 315f4190..09d53974 100644 --- a/src/pulse/timeval.h +++ b/src/pulse/timeval.h @@ -38,6 +38,8 @@ PA_C_DECL_BEGIN #define PA_USEC_PER_SEC 1000000 #define PA_NSEC_PER_SEC 1000000000 #define PA_USEC_PER_MSEC 1000 +#define PA_NSEC_PER_MSEC 1000000 +#define PA_NSEC_PER_USEC 1000 struct timeval; -- cgit From 22ceb15bae3251b95fb89c7f2451f3004f62c02f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 17 Apr 2008 16:28:23 +0000 Subject: add new rtstutter tool which can be used generate artifical scheduling latencies in the OS to trigger buffer underrun events in your software. it's an awesome debug tool for glitch-free; also move test programs from automake's check_ back to noinst_ to make sure it is built everytime Lennart presses F9 in his emacs git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2269 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 16 +++++--- src/tests/rtstutter.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 src/tests/rtstutter.c diff --git a/src/Makefile.am b/src/Makefile.am index 7139d143..45e3de71 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -226,7 +226,7 @@ pabrowse_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) # Test programs # ################################### -check_PROGRAMS = \ +noinst_PROGRAMS = \ mainloop-test \ mcalign-test \ pacat-simple \ @@ -255,16 +255,17 @@ check_PROGRAMS = \ mix-test \ remix-test \ envelope-test \ - proplist-test + proplist-test \ + rtstutter if HAVE_SIGXCPU -check_PROGRAMS += \ +noinst_PROGRAMS += \ cpulimit-test \ cpulimit-test2 endif if HAVE_GLIB20 -check_PROGRAMS += \ +noinst_PROGRAMS += \ mainloop-test-glib endif @@ -426,10 +427,15 @@ envelope_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) proplist_test_SOURCES = tests/proplist-test.c -proplist_test_LDADD = $(AM_LDADD) libpulse.la +proplist_test_LDADD = $(AM_LDADD) libpulsecore.la proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) +rtstutter_SOURCES = tests/rtstutter.c +rtstutter_LDADD = $(AM_LDADD) libpulsecore.la +rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) + ################################### # Client library # ################################### diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c new file mode 100644 index 00000000..a7d1b042 --- /dev/null +++ b/src/tests/rtstutter.c @@ -0,0 +1,107 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + Copyright 2008 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +static int msec; + +static void* work(void *p) PA_GCC_NORETURN; + +static void* work(void *p) { + cpu_set_t mask; + struct sched_param param; + + pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_INT(p)); + + memset(¶m, 0, sizeof(param)); + param.sched_priority = 12; + pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) == 0); + + CPU_ZERO(&mask); + CPU_SET(PA_PTR_TO_INT(p), &mask); + pa_assert_se(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0); + + for (;;) { + struct timespec now, end; + uint64_t nsec; + + pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_INT(p)); + sleep(1); + + pa_assert_se(clock_gettime(CLOCK_REALTIME, &end) == 0); + + nsec = (uint64_t) ((((double) rand())*msec*PA_NSEC_PER_MSEC)/RAND_MAX); + + pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_INT(p), (int) (nsec/PA_NSEC_PER_MSEC)); + + end.tv_sec += nsec / PA_NSEC_PER_SEC; + end.tv_nsec += nsec % PA_NSEC_PER_SEC; + + while (end.tv_nsec > PA_NSEC_PER_SEC) { + end.tv_sec++; + end.tv_nsec -= PA_NSEC_PER_SEC; + } + + do { + pa_assert_se(clock_gettime(CLOCK_REALTIME, &now) == 0); + } while (now.tv_sec < end.tv_sec || + (now.tv_sec == end.tv_sec && now.tv_nsec < end.tv_nsec)); + } +} + +int main(int argc, char*argv[]) { + int n; + + srand(time(NULL)); + + msec = argc > 1 ? atoi(argv[1]) : 1000; + + pa_assert(msec > 0); + + pa_log_notice("Creating random latencies of up to %ims.", msec); + + for (n = 1; n < sysconf(_SC_NPROCESSORS_CONF); n++) { + pthread_t t; + pa_assert_se(pthread_create(&t, NULL, work, PA_INT_TO_PTR(n)) == 0); + } + + work(PA_INT_TO_PTR(0)); + + return 0; +} -- cgit From 144b237d863030819bd9309148a1a264e31a4628 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:38:33 +0000 Subject: print a message on xrun git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2270 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/utils/pacat.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/utils/pacat.c b/src/utils/pacat.c index e3092138..c05bff76 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -212,6 +212,18 @@ static void stream_suspended_callback(pa_stream *s, void *userdata) { } } +static void stream_underflow_callback(pa_stream *s, void *userdata) { + assert(s); + + fprintf(stderr, "Underrun.\n"); +} + +static void stream_overflow_callback(pa_stream *s, void *userdata) { + assert(s); + + fprintf(stderr, "Overrun.\n"); +} + static void stream_moved_callback(pa_stream *s, void *userdata) { assert(s); @@ -249,6 +261,8 @@ static void context_state_callback(pa_context *c, void *userdata) { pa_stream_set_read_callback(stream, stream_read_callback, NULL); pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL); pa_stream_set_moved_callback(stream, stream_moved_callback, NULL); + pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL); + pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL); if (latency > 0) { memset(&buffer_attr, 0, sizeof(buffer_attr)); @@ -416,14 +430,14 @@ static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, /* Show the current latency */ static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) { - pa_usec_t latency, usec; + pa_usec_t l, usec; int negative = 0; assert(s); if (!success || pa_stream_get_time(s, &usec) < 0 || - pa_stream_get_latency(s, &latency, &negative) < 0) { + pa_stream_get_latency(s, &l, &negative) < 0) { fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context))); quit(1); return; @@ -431,7 +445,7 @@ static void stream_update_timing_callback(pa_stream *s, int success, void *userd fprintf(stderr, "Time: %0.3f sec; Latency: %0.0f usec. \r", (float) usec / 1000000, - (float) latency * (negative?-1:1)); + (float) l * (negative?-1:1)); } /* Someone requested that the latency is shown */ -- cgit From 4b1d684d261c52ad6ed83f1d8efdaff7105ba324 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:40:30 +0000 Subject: add new API function pa_memchunk_memcpy() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2271 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/memchunk.c | 20 ++++++++++++++++++++ src/pulsecore/memchunk.h | 3 +++ 2 files changed, 23 insertions(+) diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c index 4e73b636..16a9c140 100644 --- a/src/pulsecore/memchunk.c +++ b/src/pulsecore/memchunk.c @@ -90,3 +90,23 @@ pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c) { return (pa_memchunk*) c; } + +pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src) { + void *p, *q; + + pa_assert(dst); + pa_assert(src); + pa_assert(dst->length == src->length); + + p = pa_memblock_acquire(dst->memblock); + q = pa_memblock_acquire(src->memblock); + + memmove((uint8_t*) p + dst->index, + (uint8_t*) q + src->index, + dst->length); + + pa_memblock_release(dst->memblock); + pa_memblock_release(src->memblock); + + return dst; +} diff --git a/src/pulsecore/memchunk.h b/src/pulsecore/memchunk.h index e6105ace..46a82406 100644 --- a/src/pulsecore/memchunk.h +++ b/src/pulsecore/memchunk.h @@ -49,4 +49,7 @@ pa_memchunk* pa_memchunk_reset(pa_memchunk *c); /* Map a memory chunk back into memory if it was swapped out */ pa_memchunk *pa_memchunk_will_need(const pa_memchunk *c); +/* Copy the data in the src memchunk to the dst memchunk */ +pa_memchunk* pa_memchunk_memcpy(pa_memchunk *dst, pa_memchunk *src); + #endif -- cgit From 1ddb95a4ce337d6fbe113655437b64c293318d5f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:41:26 +0000 Subject: add new silence memblock caching subsystem git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2272 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sample-util.c | 148 ++++++++++++++++++++++++++++++++------------ src/pulsecore/sample-util.h | 16 +++-- 2 files changed, 119 insertions(+), 45 deletions(-) diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index 4a532f29..bf1f7c26 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -42,28 +42,6 @@ #define PA_SILENCE_MAX (PA_PAGE_SIZE*16) -pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length) { - pa_memblock *b; - - pa_assert(pool); - pa_assert(spec); - - if (length <= 0) - length = PA_MIN(pa_bytes_per_second(spec)/20, /* 50 ms */ - pa_mempool_block_size_max(pool)); - - if (length > PA_SILENCE_MAX) - length = PA_SILENCE_MAX; - - length = pa_frame_align(length, spec); - - b = pa_silence_memblock(pa_memblock_new(pool, length), spec); - - pa_memblock_set_is_silence(b, TRUE); - - return b; -} - pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { void *data; @@ -77,7 +55,7 @@ pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec) { return b; } -void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) { +pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) { void *data; pa_assert(c); @@ -87,37 +65,38 @@ void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec) { data = pa_memblock_acquire(c->memblock); pa_silence_memory((uint8_t*) data+c->index, c->length, spec); pa_memblock_release(c->memblock); -} -void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) { - uint8_t c = 0; - pa_assert(p); - pa_assert(length > 0); - pa_assert(spec); + return c; +} - switch (spec->format) { +static uint8_t silence_byte(pa_sample_format_t format) { + switch (format) { case PA_SAMPLE_U8: - c = 0x80; - break; + return 0x80; case PA_SAMPLE_S16LE: case PA_SAMPLE_S16BE: case PA_SAMPLE_S32LE: case PA_SAMPLE_S32BE: - case PA_SAMPLE_FLOAT32: - case PA_SAMPLE_FLOAT32RE: - c = 0; - break; + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + return 0; case PA_SAMPLE_ALAW: - c = 0xd5; - break; + return 0xd5; case PA_SAMPLE_ULAW: - c = 0xff; - break; + return 0xff; default: pa_assert_not_reached(); } + return 0; +} - memset(p, c, length); +void* pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec) { + pa_assert(p); + pa_assert(length > 0); + pa_assert(spec); + + memset(p, silence_byte(spec->format), length); + return p; } static void calc_linear_integer_stream_volumes(pa_mix_info streams[], unsigned nstreams, const pa_sample_spec *spec) { @@ -934,3 +913,90 @@ void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, } } } + +static pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) { + pa_memblock *b; + size_t length; + void *data; + + pa_assert(pool); + + length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX); + + b = pa_memblock_new(pool, length); + + data = pa_memblock_acquire(b); + memset(data, c, length); + pa_memblock_release(b); + + pa_memblock_set_is_silence(b, TRUE); + + return b; +} + +void pa_silence_cache_init(pa_silence_cache *cache) { + pa_assert(cache); + + memset(cache, 0, sizeof(pa_silence_cache)); +} + +void pa_silence_cache_done(pa_silence_cache *cache) { + pa_sample_format_t f; + pa_assert(cache); + + for (f = 0; f < PA_SAMPLE_MAX; f++) + if (cache->blocks[f]) + pa_memblock_unref(cache->blocks[f]); + + memset(cache, 0, sizeof(pa_silence_cache)); +} + +pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length) { + pa_memblock *b; + size_t l; + + pa_assert(cache); + pa_assert(pa_sample_spec_valid(spec)); + + if (!(b = cache->blocks[spec->format])) + + switch (spec->format) { + case PA_SAMPLE_U8: + cache->blocks[PA_SAMPLE_U8] = b = silence_memblock_new(pool, 0x80); + break; + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S16BE: + case PA_SAMPLE_S32LE: + case PA_SAMPLE_S32BE: + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0); + cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b); + cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b); + cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b); + cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b); + cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b); + break; + case PA_SAMPLE_ALAW: + cache->blocks[PA_SAMPLE_ALAW] = b = silence_memblock_new(pool, 0xd5); + break; + case PA_SAMPLE_ULAW: + cache->blocks[PA_SAMPLE_ULAW] = b = silence_memblock_new(pool, 0xff); + break; + default: + pa_assert_not_reached(); + } + + pa_assert(b); + + ret->memblock = pa_memblock_ref(b); + + l = pa_memblock_get_length(b); + if (length > l || length == 0) + length = l; + + ret->length = pa_frame_align(length, spec); + ret->index = 0; + + return ret; +} diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index 2ef8f924..45f00883 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -30,10 +30,18 @@ #include #include -pa_memblock *pa_silence_memblock(pa_memblock* b, const pa_sample_spec *spec); -pa_memblock *pa_silence_memblock_new(pa_mempool *pool, const pa_sample_spec *spec, size_t length); -void pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec); -void pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec); +typedef struct pa_silence_cache { + pa_memblock* blocks[PA_SAMPLE_MAX]; +} pa_silence_cache; + +void pa_silence_cache_init(pa_silence_cache *cache); +void pa_silence_cache_done(pa_silence_cache *cache); + +void *pa_silence_memory(void *p, size_t length, const pa_sample_spec *spec); +pa_memchunk* pa_silence_memchunk(pa_memchunk *c, const pa_sample_spec *spec); +pa_memblock* pa_silence_memblock(pa_memblock *b, const pa_sample_spec *spec); + +pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, pa_memchunk* ret, const pa_sample_spec *spec, size_t length); typedef struct pa_mix_info { pa_memchunk chunk; -- cgit From bee409acbe575b78559c82d6904ebcf63ddc885f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:41:53 +0000 Subject: remove debug messages git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2273 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/time-smoother.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index b44aaa50..b3f8edc5 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -333,7 +333,7 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { s->abc_valid = FALSE; - pa_log_debug("put(%llu | %llu) = %llu", x + s->time_offset, x, y); +/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */ } pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { @@ -353,7 +353,7 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { estimate(s, x, &y, NULL); - pa_log_debug("get(%llu | %llu) = %llu", x + s->time_offset, x, y); +/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */ return y; } @@ -363,7 +363,7 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) { s->time_offset = offset; - pa_log_debug("offset(%llu)", offset); +/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */ } void pa_smoother_pause(pa_smoother *s, pa_usec_t x) { @@ -372,7 +372,7 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) { if (s->paused) return; - pa_log_debug("pause(%llu)", x); +/* pa_log_debug("pause(%llu)", (unsigned long long) x); */ s->paused = TRUE; s->pause_time = x; @@ -386,7 +386,7 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x) { pa_assert(x >= s->pause_time); - pa_log_debug("resume(%llu)", x); +/* pa_log_debug("resume(%llu)", (unsigned long long) x); */ s->paused = FALSE; s->time_offset += x - s->pause_time; @@ -410,7 +410,7 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) estimate(s, x, &ney, &nde); - pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / s->dp), s->dp); +/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / s->dp), s->dp); */ return (pa_usec_t) ((double) y_delay / s->dp); } -- cgit From 687aa295a78187069ec7b44eb3ec2e6ed12106f6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:43:20 +0000 Subject: add new pa_pstream_get_shm() API, rename pa_pstream_use_shm() to pa_pstream_enable_shm(); pa_bool_t-ization git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2274 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/pstream.c | 51 ++++++++++++++++++++++++++++--------------------- src/pulsecore/pstream.h | 13 ++++++++----- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c index 97ac2b08..5004d67f 100644 --- a/src/pulsecore/pstream.c +++ b/src/pulsecore/pstream.c @@ -98,7 +98,7 @@ struct item_info { /* packet info */ pa_packet *packet; #ifdef HAVE_CREDS - int with_creds; + pa_bool_t with_creds; pa_creds creds; #endif @@ -121,7 +121,7 @@ struct pa_pstream { pa_queue *send_queue; - int dead; + pa_bool_t dead; struct { pa_pstream_descriptor descriptor; @@ -141,7 +141,7 @@ struct pa_pstream { size_t index; } read; - int use_shm; + pa_bool_t use_shm; pa_memimport *import; pa_memexport *export; @@ -167,7 +167,7 @@ struct pa_pstream { #ifdef HAVE_CREDS pa_creds read_creds, write_creds; - int read_creds_valid, send_creds_now; + pa_bool_t read_creds_valid, send_creds_now; #endif }; @@ -239,7 +239,7 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo PA_REFCNT_INIT(p); p->io = io; pa_iochannel_set_callback(io, io_callback, p); - p->dead = 0; + p->dead = FALSE; p->mainloop = m; p->defer_event = m->defer_new(m, defer_callback, p); @@ -269,18 +269,18 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo p->mempool = pool; - p->use_shm = 0; + p->use_shm = FALSE; p->export = NULL; /* We do importing unconditionally */ p->import = pa_memimport_new(p->mempool, memimport_release_cb, p); - pa_iochannel_socket_set_rcvbuf(io, 1024*8); - pa_iochannel_socket_set_sndbuf(io, 1024*8); + pa_iochannel_socket_set_rcvbuf(io, pa_mempool_block_size_max(p->mempool)); + pa_iochannel_socket_set_sndbuf(io, pa_mempool_block_size_max(p->mempool)); #ifdef HAVE_CREDS - p->send_creds_now = 0; - p->read_creds_valid = 0; + p->send_creds_now = FALSE; + p->read_creds_valid = FALSE; #endif return p; } @@ -383,7 +383,7 @@ void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa i->offset = offset; i->seek_mode = seek_mode; #ifdef HAVE_CREDS - i->with_creds = 0; + i->with_creds = FALSE; #endif pa_queue_push(p->send_queue, i); @@ -410,7 +410,7 @@ void pa_pstream_send_release(pa_pstream *p, uint32_t block_id) { item->type = PA_PSTREAM_ITEM_SHMRELEASE; item->block_id = block_id; #ifdef HAVE_CREDS - item->with_creds = 0; + item->with_creds = FALSE; #endif pa_queue_push(p->send_queue, item); @@ -447,7 +447,7 @@ void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id) { item->type = PA_PSTREAM_ITEM_SHMREVOKE; item->block_id = block_id; #ifdef HAVE_CREDS - item->with_creds = 0; + item->with_creds = FALSE; #endif pa_queue_push(p->send_queue, item); @@ -504,7 +504,7 @@ static void prepare_next_write_item(pa_pstream *p) { } else { uint32_t flags; - int send_payload = 1; + pa_bool_t send_payload = TRUE; pa_assert(p->write.current->type == PA_PSTREAM_ITEM_MEMBLOCK); pa_assert(p->write.current->chunk.memblock); @@ -529,7 +529,7 @@ static void prepare_next_write_item(pa_pstream *p) { &length) >= 0) { flags |= PA_FLAG_SHMDATA; - send_payload = 0; + send_payload = FALSE; p->write.shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id); p->write.shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id); @@ -599,7 +599,7 @@ static int do_write(pa_pstream *p) { if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_creds)) < 0) goto fail; - p->send_creds_now = 0; + p->send_creds_now = FALSE; } else #endif @@ -875,7 +875,7 @@ frame_done: p->read.data = NULL; #ifdef HAVE_CREDS - p->read_creds_valid = 0; + p->read_creds_valid = FALSE; #endif return 0; @@ -935,14 +935,14 @@ void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, p->release_callback_userdata = userdata; } -int pa_pstream_is_pending(pa_pstream *p) { - int b; +pa_bool_t pa_pstream_is_pending(pa_pstream *p) { + pa_bool_t b; pa_assert(p); pa_assert(PA_REFCNT_VALUE(p) > 0); if (p->dead) - b = 0; + b = FALSE; else b = p->write.current || !pa_queue_is_empty(p->send_queue); @@ -971,7 +971,7 @@ void pa_pstream_unlink(pa_pstream *p) { if (p->dead) return; - p->dead = 1; + p->dead = TRUE; if (p->import) { pa_memimport_free(p->import); @@ -999,7 +999,7 @@ void pa_pstream_unlink(pa_pstream *p) { p->recieve_memblock_callback = NULL; } -void pa_pstream_use_shm(pa_pstream *p, int enable) { +void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable) { pa_assert(p); pa_assert(PA_REFCNT_VALUE(p) > 0); @@ -1018,3 +1018,10 @@ void pa_pstream_use_shm(pa_pstream *p, int enable) { } } } + +pa_bool_t pa_pstream_get_shm(pa_pstream *p) { + pa_assert(p); + pa_assert(PA_REFCNT_VALUE(p) > 0); + + return p->use_shm; +} diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h index 72babea9..00b37b71 100644 --- a/src/pulsecore/pstream.h +++ b/src/pulsecore/pstream.h @@ -35,6 +35,7 @@ #include #include #include +#include typedef struct pa_pstream pa_pstream; @@ -44,8 +45,11 @@ typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata); typedef void (*pa_pstream_block_id_cb_t)(pa_pstream *p, uint32_t block_id, void *userdata); pa_pstream* pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *p); -void pa_pstream_unref(pa_pstream*p); + pa_pstream* pa_pstream_ref(pa_pstream*p); +void pa_pstream_unref(pa_pstream*p); + +void pa_pstream_unlink(pa_pstream *p); void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_creds *creds); void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk); @@ -59,10 +63,9 @@ void pa_pstream_set_die_callback(pa_pstream *p, pa_pstream_notify_cb_t cb, void void pa_pstream_set_release_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata); void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb, void *userdata); -int pa_pstream_is_pending(pa_pstream *p); - -void pa_pstream_use_shm(pa_pstream *p, int enable); +pa_bool_t pa_pstream_is_pending(pa_pstream *p); -void pa_pstream_unlink(pa_pstream *p); +void pa_pstream_enable_shm(pa_pstream *p, pa_bool_t enable); +pa_bool_t pa_pstream_get_shm(pa_pstream *p); #endif -- cgit From af256978debba3f1149eb314eba51c2ca5716379 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:43:59 +0000 Subject: follow pa_pstream_use_shm->pa_pstream_enable_shm rename git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2275 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/context.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulse/context.c b/src/pulse/context.c index abc6a855..7806e88c 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -427,7 +427,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t const pa_creds *creds; if ((creds = pa_pdispatch_creds(pd))) if (getuid() == creds->uid) - pa_pstream_use_shm(c->pstream, 1); + pa_pstream_enable_shm(c->pstream, TRUE); #endif } -- cgit From 03df08872df4bd6b1266b1928bb74d8595dcc2c8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:44:33 +0000 Subject: add lower boundary for artifical latencies git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2276 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/rtstutter.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c index a7d1b042..39dfc5d3 100644 --- a/src/tests/rtstutter.c +++ b/src/tests/rtstutter.c @@ -39,7 +39,7 @@ #include #include -static int msec; +static int msec_lower, msec_upper; static void* work(void *p) PA_GCC_NORETURN; @@ -66,7 +66,9 @@ static void* work(void *p) { pa_assert_se(clock_gettime(CLOCK_REALTIME, &end) == 0); - nsec = (uint64_t) ((((double) rand())*msec*PA_NSEC_PER_MSEC)/RAND_MAX); + nsec = + (uint64_t) ((((double) rand())*(msec_upper-msec_lower)*PA_NSEC_PER_MSEC)/RAND_MAX) + + (uint64_t) (msec_lower*PA_NSEC_PER_MSEC); pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_INT(p), (int) (nsec/PA_NSEC_PER_MSEC)); @@ -90,11 +92,21 @@ int main(int argc, char*argv[]) { srand(time(NULL)); - msec = argc > 1 ? atoi(argv[1]) : 1000; + if (argc >= 3) { + msec_lower = atoi(argv[1]); + msec_upper = atoi(argv[2]); + } else if (argc >= 2) { + msec_lower = 0; + msec_upper = atoi(argv[1]); + } else { + msec_lower = 0; + msec_upper = 1000; + } - pa_assert(msec > 0); + pa_assert(msec_upper > 0); + pa_assert(msec_upper >= msec_lower); - pa_log_notice("Creating random latencies of up to %ims.", msec); + pa_log_notice("Creating random latencies in the range of %ims to %ims.", msec_lower, msec_upper); for (n = 1; n < sysconf(_SC_NPROCESSORS_CONF); n++) { pthread_t t; -- cgit From ed36f3129cf4ff88cd99c39ac9708b639e172c9f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:46:43 +0000 Subject: increase the default pool size to 16MB because we now need to keep a lot more memory around due to glitch-free. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2277 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/memblock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index b76980ff..c452ae0b 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -46,8 +46,8 @@ #include "memblock.h" -#define PA_MEMPOOL_SLOTS_MAX 128 -#define PA_MEMPOOL_SLOT_SIZE (16*1024) +#define PA_MEMPOOL_SLOTS_MAX 512 +#define PA_MEMPOOL_SLOT_SIZE (32*1024) #define PA_MEMEXPORT_SLOTS_MAX 128 @@ -253,7 +253,7 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) { slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx)); if (!slot) { - pa_log_debug("Pool full"); + pa_log_info("Pool full"); pa_atomic_inc(&p->stat.n_pool_full); return NULL; } -- cgit From 33cb5897e17e42e07512452cea587c2b1ffbeaba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:49:55 +0000 Subject: split user supplied data in multiple memory blocks if necessary to fit in one mempool tile. If the caller supplied a free_cb and we use shm it's better to copy the data immediately to the shm region instead of keeping it around as user memblock git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2278 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/stream.c | 51 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 3d794876..f047acfe 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -896,6 +896,10 @@ int pa_stream_write( pa_seek_mode_t seek) { pa_memchunk chunk; + pa_seek_mode_t t_seek; + int64_t t_offset; + size_t t_length; + const void *t_data; pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); @@ -909,21 +913,42 @@ int pa_stream_write( if (length <= 0) return 0; - if (free_cb) - chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) data, length, free_cb, 1); - else { - void *tdata; - chunk.memblock = pa_memblock_new(s->context->mempool, length); - tdata = pa_memblock_acquire(chunk.memblock); - memcpy(tdata, data, length); - pa_memblock_release(chunk.memblock); - } + t_seek = seek; + t_offset = offset; + t_length = length; + t_data = data; + + while (t_length > 0) { + + chunk.index = 0; + + if (free_cb && !pa_pstream_get_shm(s->context->pstream)) { + chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1); + chunk.length = t_length; + } else { + void *d; + + chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool)); + chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length); - chunk.index = 0; - chunk.length = length; + d = pa_memblock_acquire(chunk.memblock); + memcpy(d, t_data, chunk.length); + pa_memblock_release(chunk.memblock); + } + + pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk); + + t_offset = 0; + t_seek = PA_SEEK_RELATIVE; + + t_data = (const uint8_t*) t_data + chunk.length; + t_length -= chunk.length; + + pa_memblock_unref(chunk.memblock); + } - pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk); - pa_memblock_unref(chunk.memblock); + if (free_cb && pa_pstream_get_shm(s->context->pstream)) + free_cb((void*) data); if (length < s->requested_bytes) s->requested_bytes -= length; -- cgit From 33a35b6f2ef6edbf0e2b680c7883616bcfeb2728 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:50:42 +0000 Subject: update to recent changes of proplist api git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2279 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/proplist-test.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c index b88f4e5e..5f7a78f3 100644 --- a/src/tests/proplist-test.c +++ b/src/tests/proplist-test.c @@ -28,25 +28,26 @@ #include #include #include +#include int main(int argc, char*argv[]) { pa_proplist *a, *b; char *s, *t; a = pa_proplist_new(); - pa_assert_se(pa_proplist_puts(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0); - pa_assert_se(pa_proplist_puts(a, PA_PROP_MEDIA_ARTIST, "Johann Sebastian Bach") == 0); + pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0); + pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_ARTIST, "Johann Sebastian Bach") == 0); b = pa_proplist_new(); - pa_assert_se(pa_proplist_puts(b, PA_PROP_MEDIA_TITLE, "Goldbergvariationen") == 0); - pa_assert_se(pa_proplist_put(b, PA_PROP_MEDIA_ICON, "\0\1\2\3\4\5\6\7", 8) == 0); + pa_assert_se(pa_proplist_sets(b, PA_PROP_MEDIA_TITLE, "Goldbergvariationen") == 0); + pa_assert_se(pa_proplist_set(b, PA_PROP_MEDIA_ICON, "\0\1\2\3\4\5\6\7", 8) == 0); - pa_proplist_merge(a, b); + pa_proplist_update(a, PA_UPDATE_MERGE, b); pa_assert_se(!pa_proplist_gets(a, PA_PROP_MEDIA_ICON)); printf("%s\n", pa_strnull(pa_proplist_gets(a, PA_PROP_MEDIA_TITLE))); - pa_assert_se(pa_proplist_remove(b, PA_PROP_MEDIA_TITLE) == 0); + pa_assert_se(pa_proplist_unset(b, PA_PROP_MEDIA_TITLE) == 0); s = pa_proplist_to_string(a); t = pa_proplist_to_string(b); -- cgit From d1d7a0749d95701aac226895319b8dee044c3da7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:51:08 +0000 Subject: we have not periodic timers anymore git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2280 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/rtpoll-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/rtpoll-test.c b/src/tests/rtpoll-test.c index e6493771..af942104 100644 --- a/src/tests/rtpoll-test.c +++ b/src/tests/rtpoll-test.c @@ -67,7 +67,7 @@ int main(int argc, char *argv[]) { pa_rtpoll_item_set_before_callback(w, worker); pa_rtpoll_install(p); - pa_rtpoll_set_timer_periodic(p, 10000000); /* 10 s */ + pa_rtpoll_set_timer_relative(p, 10000000); /* 10 s */ pa_rtpoll_run(p, 1); -- cgit From a0671aa8db8d92ae33dc0e8003cd793635098201 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:52:05 +0000 Subject: fix for new location of gccmacro.h git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2281 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/channelmap-test.c | 2 +- src/tests/cpulimit-test.c | 2 +- src/tests/mainloop-test.c | 2 +- src/tests/mcalign-test.c | 3 ++- src/tests/pacat-simple.c | 2 +- src/tests/parec-simple.c | 2 +- src/tests/strlist-test.c | 3 ++- src/tests/thread-mainloop-test.c | 2 +- src/tests/voltest.c | 2 +- 9 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/tests/channelmap-test.c b/src/tests/channelmap-test.c index 98f36b61..d26d2cff 100644 --- a/src/tests/channelmap-test.c +++ b/src/tests/channelmap-test.c @@ -4,7 +4,7 @@ #include #include -#include +#include int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { char cm[PA_CHANNEL_MAP_SNPRINT_MAX]; diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c index d582e9c5..4563c0f6 100644 --- a/src/tests/cpulimit-test.c +++ b/src/tests/cpulimit-test.c @@ -30,7 +30,7 @@ #include #include -#include +#include #ifdef TEST2 #include diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c index c386251c..79a4aaa0 100644 --- a/src/tests/mainloop-test.c +++ b/src/tests/mainloop-test.c @@ -29,9 +29,9 @@ #include #include +#include #include -#include #ifdef GLIB_MAIN_LOOP diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c index d1013118..79dd5797 100644 --- a/src/tests/mcalign-test.c +++ b/src/tests/mcalign-test.c @@ -31,9 +31,10 @@ #include #include +#include + #include #include -#include /* A simple program for testing pa_mcalign */ diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c index 2da67c1a..c2123b74 100644 --- a/src/tests/pacat-simple.c +++ b/src/tests/pacat-simple.c @@ -31,7 +31,7 @@ #include #include -#include +#include #define BUFSIZE 1024 diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c index d7d88360..9c66cc23 100644 --- a/src/tests/parec-simple.c +++ b/src/tests/parec-simple.c @@ -30,7 +30,7 @@ #include #include -#include +#include #define BUFSIZE 1024 diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c index 47770b5d..2bd1645c 100644 --- a/src/tests/strlist-test.c +++ b/src/tests/strlist-test.c @@ -1,8 +1,9 @@ #include #include +#include + #include -#include int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char* argv[]) { char *t, *u; diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c index 558e53a5..ac6d5049 100644 --- a/src/tests/thread-mainloop-test.c +++ b/src/tests/thread-mainloop-test.c @@ -30,8 +30,8 @@ #include #include #include +#include -#include #include static void tcb(pa_mainloop_api*a, pa_time_event *e, const struct timeval *tv, void *userdata) { diff --git a/src/tests/voltest.c b/src/tests/voltest.c index dcc1ec51..91752ad9 100644 --- a/src/tests/voltest.c +++ b/src/tests/voltest.c @@ -3,7 +3,7 @@ #include #include -#include +#include int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char *argv[]) { pa_volume_t v; -- cgit From 7556ef5bfc37c99064d95857626bcf9f20423c70 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 19:53:07 +0000 Subject: maintain a global silence memblock cache git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2282 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/core.c | 2 ++ src/pulsecore/core.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index cf018509..3b758a38 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -125,6 +125,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, int shared) { c->subscription_event_last = NULL; c->mempool = pool; + pa_silence_cache_init(&c->silence_cache); c->quit_event = NULL; @@ -188,6 +189,7 @@ static void core_free(pa_object *o) { pa_xfree(c->default_source_name); pa_xfree(c->default_sink_name); + pa_silence_cache_done(&c->silence_cache); pa_mempool_free(c->mempool); pa_property_cleanup(c); diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 6be1a0c5..50c05b4c 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -35,6 +35,7 @@ #include #include #include +#include typedef struct pa_core pa_core; @@ -112,6 +113,7 @@ struct pa_core { pa_subscription_event *subscription_event_last; pa_mempool *mempool; + pa_silence_cache silence_cache; int exit_idle_time, module_idle_time, scache_idle_time; -- cgit From 62e7bc17c41c5542779a3c395a9d47d2bd306de2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 20:16:55 +0000 Subject: Big pile of dependant changes: * Change pa_memblockq to carry silence memchunk instead of memblock and adapt all users * Add new call pa_sink_input_get_silence() to get the suitable silence block for a sink input * Implement monitoring sources properly by adding a delay queue to even out rewinds * Remove pa_{sink|source}_ping() becaused unnecessary these days and not used * Fix naming of various rewind related functions. Downstream is now _request_rewind(), upstream is _process_rewind() * Fix volume adjustments for a single stream in pa_sink_render() * Properly handle prebuf-style buffer underruns in pa_sink_input * Don't allow rewinding to more than the last underrun * Rework default buffering metrics selection for native protocol * New functions pa_memblockq_prebuf_active(), pa_memblockq_silence() * add option "mixer_reset=" to module-alsa-sink * Other cleanups git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2283 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 232 ++++++++++++++++++++------------ src/modules/module-alsa-source.c | 51 ++++--- src/modules/module-combine.c | 3 +- src/modules/module-ladspa-sink.c | 5 +- src/modules/module-remap-sink.c | 5 +- src/modules/rtp/module-rtp-recv.c | 11 +- src/pulsecore/memblockq.c | 102 ++++++++------ src/pulsecore/memblockq.h | 11 +- src/pulsecore/play-memblockq.c | 12 +- src/pulsecore/protocol-native.c | 276 +++++++++++++++++++++++++++----------- src/pulsecore/sink-input.c | 224 ++++++++++++++++++++----------- src/pulsecore/sink-input.h | 20 +-- src/pulsecore/sink.c | 153 ++++++++++++--------- src/pulsecore/sink.h | 16 +-- src/pulsecore/sound-file-stream.c | 12 +- src/pulsecore/source-output.c | 161 +++++++++++++++++----- src/pulsecore/source-output.h | 14 ++ src/pulsecore/source.c | 121 +++++++++++------ src/pulsecore/source.h | 12 +- src/tests/memblockq-test.c | 12 +- 20 files changed, 936 insertions(+), 517 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index f8a2c48b..32bfc304 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -70,11 +70,30 @@ PA_MODULE_USAGE( "mmap= " "tsched= " "tsched_buffer_size= " - "tsched_buffer_watermark="); + "tsched_buffer_watermark= " + "mixer_reset="); + +static const char* const valid_modargs[] = { + "sink_name", + "device", + "device_id", + "format", + "rate", + "channels", + "channel_map", + "fragments", + "fragment_size", + "mmap", + "tsched", + "tsched_buffer_size", + "tsched_buffer_watermark", + "mixer_reset", + NULL +}; #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) +#define DEFAULT_TSCHED_BUFFER_USEC (10*PA_USEC_PER_SEC) /* 10s */ +#define DEFAULT_TSCHED_WATERMARK_USEC (200*PA_USEC_PER_MSEC) /* 20ms */ struct userdata { pa_core *core; @@ -112,23 +131,7 @@ struct userdata { int64_t frame_index; snd_pcm_sframes_t hwbuf_unused_frames; -}; - -static const char* const valid_modargs[] = { - "sink_name", - "device", - "device_id", - "format", - "rate", - "channels", - "channel_map", - "fragments", - "fragment_size", - "mmap", - "tsched", - "tsched_buffer_size", - "tsched_buffer_watermark", - NULL + snd_pcm_sframes_t avail_min_frames; }; static int mmap_write(struct userdata *u) { @@ -144,6 +147,7 @@ static int mmap_write(struct userdata *u) { int err; const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset, frames; + size_t left_to_play; snd_pcm_hwsync(u->pcm_handle); @@ -177,8 +181,26 @@ static int mmap_write(struct userdata *u) { if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) return work_done; + if (n*u->frame_size < u->hwbuf_size) + left_to_play = u->hwbuf_size - (n*u->frame_size); + else + left_to_play = 0; + + pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + + if (left_to_play <= 0 && !u->first) { + u->tsched_watermark *=2; + + if (u->tsched_watermark >= u->hwbuf_size) + u->tsched_watermark = u->hwbuf_size-u->frame_size; + + pa_log_notice("Underrun! Increasing wakeup watermark to %0.2f", (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + } + frames = n = n - u->hwbuf_unused_frames; + pa_log_debug("%llu frames to write", (unsigned long long) frames); + if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err)); @@ -357,14 +379,21 @@ static void update_smoother(struct userdata *u) { snd_pcm_hwsync(u->pcm_handle); snd_pcm_avail_update(u->pcm_handle); - if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { +/* if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { */ +/* pa_log("Failed to query DSP status data: %s", snd_strerror(err)); */ +/* return; */ +/* } */ + +/* delay = snd_pcm_status_get_delay(status); */ + + if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { pa_log("Failed to query DSP status data: %s", snd_strerror(err)); return; } - delay = snd_pcm_status_get_delay(status); + frames = u->frame_index - delay; - pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); +/* pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); */ /* snd_pcm_status_get_tstamp(status, ×tamp); */ /* pa_rtclock_from_wallclock(×tamp); */ @@ -450,7 +479,7 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { if (usec <= 0) usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec); - pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); +/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec); @@ -459,66 +488,58 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { else usec /= 2; - pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); +/* pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ return usec; } -static void update_hwbuf_unused_frames(struct userdata *u) { - pa_usec_t usec; - size_t b; +static int update_sw_params(struct userdata *u) { + int err; + pa_usec_t latency; pa_assert(u); - if ((usec = pa_sink_get_requested_latency_within_thread(u->sink)) <= 0) { - /* Use the full buffer if noone asked us for anything - * specific */ - u->hwbuf_unused_frames = 0; - return; - } + /* Use the full buffer if noone asked us for anything specific */ + u->hwbuf_unused_frames = 0; - b = pa_usec_to_bytes(usec, &u->sink->sample_spec); + if (u->use_tsched) + if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) > 0) { + size_t b; - /* We need at least one sample in our buffer */ + pa_log("latency set to %llu", (unsigned long long) latency); - if (PA_UNLIKELY(b < u->frame_size)) - b = u->frame_size; + b = pa_usec_to_bytes(latency, &u->sink->sample_spec); - u->hwbuf_unused_frames = - PA_LIKELY(b < u->hwbuf_size) ? - ((u->hwbuf_size - b) / u->frame_size) : 0; -} + /* We need at least one sample in our buffer */ -static int update_sw_params(struct userdata *u) { - snd_pcm_uframes_t avail_min; - int err; + if (PA_UNLIKELY(b < u->frame_size)) + b = u->frame_size; - pa_assert(u); + u->hwbuf_unused_frames = + PA_LIKELY(b < u->hwbuf_size) ? + ((u->hwbuf_size - b) / u->frame_size) : 0; + } + + pa_log("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames); + + /* We need at last one frame in the used part of the buffer */ + u->avail_min_frames = u->hwbuf_unused_frames + 1; if (u->use_tsched) { pa_usec_t usec; usec = hw_sleep_time(u); - avail_min = pa_usec_to_bytes(usec, &u->sink->sample_spec) / u->frame_size; - - if (avail_min <= 0) - avail_min = 1; - - } else - avail_min = 1; + u->avail_min_frames += (pa_usec_to_bytes(usec, &u->sink->sample_spec) / u->frame_size); + } - pa_log("setting avail_min=%lu", (unsigned long) avail_min); + pa_log("setting avail_min=%lu", (unsigned long) u->avail_min_frames); - if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { + if ((err = pa_alsa_set_sw_params(u->pcm_handle, u->avail_min_frames)) < 0) { pa_log("Failed to set software parameters: %s", snd_strerror(err)); return err; } - update_hwbuf_unused_frames(u); - - pa_log("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames); - return 0; } @@ -808,23 +829,23 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); -/* update_hwbuf_unused_frames(u); */ - for (;;) { int ret; - pa_log_debug("loop"); +/* pa_log_debug("loop"); */ /* Render some data and write it to the dsp */ if (PA_SINK_OPENED(u->sink->thread_info.state)) { int work_done = 0; - if (u->sink->thread_info.rewind_nbytes > 0 && u->use_tsched) { - snd_pcm_sframes_t frames, limit, unused; + if (u->sink->thread_info.rewind_nbytes > 0) { + snd_pcm_sframes_t unused; + size_t rewind_nbytes, unused_nbytes, limit_nbytes; - pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) u->sink->thread_info.rewind_nbytes); + rewind_nbytes = u->sink->thread_info.rewind_nbytes; + u->sink->thread_info.rewind_nbytes = 0; - frames = u->sink->thread_info.rewind_nbytes / u->frame_size; + pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); snd_pcm_hwsync(u->pcm_handle); if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) { @@ -832,32 +853,54 @@ static void thread_func(void *userdata) { goto fail; } - limit = (u->hwbuf_size / u->frame_size) - unused; + unused_nbytes = u->tsched_watermark + (size_t) unused * u->frame_size; - if (frames > limit) - frames = limit; + if (u->hwbuf_size > unused_nbytes) + limit_nbytes = u->hwbuf_size - unused_nbytes; + else + limit_nbytes = 0; - frames = 0; + if (rewind_nbytes > limit_nbytes) + rewind_nbytes = limit_nbytes; - if (frames > 0) { + if (rewind_nbytes > 0) { + snd_pcm_sframes_t in_frames, out_frames; - pa_log_debug("Limited to %lu bytes.", (unsigned long) frames * u->frame_size); + pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes); - if ((frames = snd_pcm_rewind(u->pcm_handle, frames)) < 0) { - pa_log("snd_pcm_rewind() failed: %s", snd_strerror(frames)); + in_frames = (snd_pcm_sframes_t) rewind_nbytes / u->frame_size; + pa_log_debug("before: %lu", (unsigned long) in_frames); + if ((out_frames = snd_pcm_rewind(u->pcm_handle, in_frames)) < 0) { + pa_log("snd_pcm_rewind() failed: %s", snd_strerror(out_frames)); goto fail; } + pa_log_debug("after: %lu", (unsigned long) out_frames); + + if (out_frames > in_frames) { + snd_pcm_sframes_t sfix; + pa_log("FUCK, device rewound %lu frames more than we wanted. What a mess!", (unsigned long) (out_frames-in_frames)); + + if ((sfix = snd_pcm_forward(u->pcm_handle, out_frames-in_frames)) < 0) { + pa_log("snd_pcm_forward() failed: %s", snd_strerror(sfix)); + goto fail; + } + + pa_log("Could fix by %lu", (unsigned long) sfix); + out_frames -= sfix; + } - if ((u->sink->thread_info.rewind_nbytes = frames * u->frame_size) <= 0) + rewind_nbytes = out_frames * u->frame_size; + + if (rewind_nbytes <= 0) pa_log_info("Tried rewind, but was apparently not possible."); else { - u->frame_index -= frames; - pa_log_debug("Rewound %lu bytes.", (unsigned long) u->sink->thread_info.rewind_nbytes); - pa_sink_process_rewind(u->sink); + u->frame_index -= out_frames; + pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); + pa_sink_process_rewind(u->sink, rewind_nbytes); } - } else { + } else pa_log_debug("Mhmm, actually there is nothing to rewind."); - } + } if (u->use_mmap) { @@ -868,7 +911,7 @@ static void thread_func(void *userdata) { goto fail; } - pa_log_debug("work_done = %i", work_done); +/* pa_log_debug("work_done = %i", work_done); */ if (work_done) { @@ -891,13 +934,13 @@ static void thread_func(void *userdata) { usec = hw_sleep_time(u); - pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) usec / PA_USEC_PER_MSEC); +/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) usec / PA_USEC_PER_MSEC); */ /* Convert from the sound card time domain to the * system time domain */ cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), usec); - pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); +/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */ /* We don't trust the conversion, so we wake up whatever comes first */ pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); @@ -974,7 +1017,8 @@ static void thread_func(void *userdata) { u->first = TRUE; } - pa_log_debug("alsa revents = %i", revents); + if (revents) + pa_log_info("Wakeup from ALSA! (%i)", revents); } } @@ -1003,7 +1047,7 @@ int pa__init(pa_module*m) { const char *name; char *name_buf = NULL; pa_bool_t namereg_fail; - pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d; + pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE; pa_usec_t usec; pa_sink_new_data data; static const char * const class_table[SND_PCM_CLASS_LAST+1] = { @@ -1066,6 +1110,11 @@ int pa__init(pa_module*m) { use_tsched = FALSE; } + if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) { + pa_log("Failed to parse mixer_reset argument."); + goto fail; + } + u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; @@ -1202,7 +1251,7 @@ int pa__init(pa_module*m) { if (class_table[snd_pcm_info_get_class(pcm_info)]) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, class_table[snd_pcm_info_get_class(pcm_info)]); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap_rewrite" : (u->use_mmap ? "mmap" : "serial")); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); pa_sink_new_data_done(&data); @@ -1225,14 +1274,18 @@ int pa__init(pa_module*m) { u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; u->hwbuf_unused_frames = 0; + u->avail_min_frames = 0; u->tsched_watermark = tsched_watermark; u->frame_index = 0; u->hw_dB_supported = FALSE; u->hw_dB_min = u->hw_dB_max = 0; u->hw_volume_min = u->hw_volume_max = 0; - u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0; + if (use_tsched) + if (u->tsched_watermark >= u->hwbuf_size/2) + u->tsched_watermark = pa_frame_align(u->hwbuf_size/2, &ss); + u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0; u->sink->max_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); if (!use_tsched) @@ -1310,10 +1363,11 @@ int pa__init(pa_module*m) { u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0); pa_log_info("Using hardware volume control. %s dB scale.", u->hw_dB_supported ? "Using" : "Not using"); - } else { + } else if (mixer_reset) { pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); pa_alsa_0dB_playback(u->mixer_elem); - } + } else + pa_log_info("Using software volume control. Leaving hw mixer controls untouched."); } if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) { diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 83b7f53a..185acc01 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -71,7 +71,26 @@ PA_MODULE_USAGE( "mmap= " "tsched= " "tsched_buffer_size= " - "tsched_buffer_watermark="); + "tsched_buffer_watermark= " + "mixer_reset="); + +static const char* const valid_modargs[] = { + "source_name", + "device", + "device_id", + "format", + "rate", + "channels", + "channel_map", + "fragments", + "fragment_size", + "mmap", + "tsched", + "tsched_buffer_size", + "tsched_buffer_watermark", + "mixer_reset", + NULL +}; #define DEFAULT_DEVICE "default" #define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) @@ -110,23 +129,6 @@ struct userdata { int64_t frame_index; }; -static const char* const valid_modargs[] = { - "source_name", - "device", - "device_id", - "format", - "rate", - "channels", - "channel_map", - "fragments", - "fragment_size", - "mmap", - "tsched", - "tsched_buffer_size", - "tsched_buffer_watermark", - NULL -}; - static int mmap_read(struct userdata *u) { int work_done = 0; @@ -876,7 +878,7 @@ int pa__init(pa_module*m) { const char *name; char *name_buf = NULL; pa_bool_t namereg_fail; - pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d; + pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE; pa_source_new_data data; static const char * const class_table[SND_PCM_CLASS_LAST+1] = { [SND_PCM_CLASS_GENERIC] = "sound", @@ -938,6 +940,11 @@ int pa__init(pa_module*m) { use_tsched = FALSE; } + if (pa_modargs_get_value_boolean(ma, "mixer_reset", &mixer_reset) < 0) { + pa_log("Failed to parse mixer_reset argument."); + goto fail; + } + u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; @@ -1163,10 +1170,12 @@ int pa__init(pa_module*m) { u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0); pa_log_info("Using hardware volume control. %s dB scale.", u->hw_dB_supported ? "Using" : "Not using"); - } else { + } else if (mixer_reset) { pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); pa_alsa_0dB_capture(u->mixer_elem); - } + } else + pa_log_info("Using software volume control. Leaving hw mixer controls untouched."); + } diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 0b17bc5a..6f99ae43 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -252,7 +252,8 @@ static void thread_func(void *userdata) { /* Just rewind if necessary, since we are in NULL mode, we * don't have to pass this on */ - pa_sink_process_rewind(u->sink); + pa_sink_process_rewind(u->sink, u->sink->thread_info.rewind_nbytes); + u->sink->thread_info.rewind_nbytes = 0; pa_rtclock_get(&now); diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 2342cde9..14f14b14 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -134,7 +134,7 @@ static void sink_request_rewind(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - pa_sink_input_request_rewrite(u->sink_input, s->thread_info.rewind_nbytes); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE); } /* Called from I/O thread context */ @@ -224,8 +224,7 @@ static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_assert_se(u = i->userdata); pa_assert(nbytes > 0); - u->sink->thread_info.rewind_nbytes = nbytes; - pa_sink_process_rewind(u->sink); + pa_sink_process_rewind(u->sink, nbytes); } /* Called from I/O thread context */ diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 2dc4f2a1..3f70ee1d 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -112,7 +112,7 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - pa_sink_input_request_rewrite(u->sink_input, s->thread_info.rewind_nbytes); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE); } /* Called from I/O thread context */ @@ -166,8 +166,7 @@ static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_assert_se(u = i->userdata); pa_assert(nbytes > 0); - u->sink->thread_info.rewind_nbytes = nbytes; - pa_sink_process_rewind(u->sink); + pa_sink_process_rewind(u->sink, nbytes); } /* Called from I/O thread context */ diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index be54c385..92b43dd9 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -313,7 +313,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in char *c; pa_sink *sink; int fd = -1; - pa_memblock *silence; + pa_memchunk silence; pa_sink_input_new_data data; struct timeval now; @@ -371,10 +371,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in s->sink_input->attach = sink_input_attach; s->sink_input->detach = sink_input_detach; - silence = pa_silence_memblock_new( - s->userdata->module->core->mempool, - &s->sink_input->sample_spec, - pa_frame_align(pa_bytes_per_second(&s->sink_input->sample_spec)/128, &s->sink_input->sample_spec)); + pa_sink_input_get_silence(s->sink_input, &silence); s->memblockq = pa_memblockq_new( 0, @@ -384,9 +381,9 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in pa_bytes_per_second(&s->sink_input->sample_spec)/10+1, 0, 0, - silence); + &silence); - pa_memblock_unref(silence); + pa_memblock_unref(silence.memblock); pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec)); diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index 1d9583e1..947c69a2 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -55,7 +55,7 @@ struct pa_memblockq { size_t maxlength, tlength, base, prebuf, minreq, maxrewind; int64_t read_index, write_index; pa_bool_t in_prebuf; - pa_memblock *silence; + pa_memchunk silence; pa_mcalign *mcalign; int64_t missing; size_t requested; @@ -69,7 +69,7 @@ pa_memblockq* pa_memblockq_new( size_t prebuf, size_t minreq, size_t maxrewind, - pa_memblock *silence) { + pa_memchunk *silence) { pa_memblockq* bq; @@ -98,7 +98,12 @@ pa_memblockq* pa_memblockq_new( pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu", (unsigned long) bq->maxlength, (unsigned long) bq->tlength, (unsigned long) bq->base, (unsigned long) bq->prebuf, (unsigned long) bq->minreq, (unsigned long) bq->maxrewind); - bq->silence = silence ? pa_memblock_ref(silence) : NULL; + if (silence) { + bq->silence = *silence; + pa_memblock_ref(bq->silence.memblock); + } else + pa_memchunk_reset(&bq->silence); + bq->mcalign = pa_mcalign_new(bq->base); return bq; @@ -109,8 +114,8 @@ void pa_memblockq_free(pa_memblockq* bq) { pa_memblockq_flush(bq); - if (bq->silence) - pa_memblock_unref(bq->silence); + if (bq->silence.memblock) + pa_memblock_unref(bq->silence.memblock); if (bq->mcalign) pa_mcalign_free(bq->mcalign); @@ -420,7 +425,7 @@ finish: return 0; } -static pa_bool_t memblockq_check_prebuf(pa_memblockq *bq) { +pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq) { pa_assert(bq); if (bq->in_prebuf) { @@ -447,7 +452,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { pa_assert(chunk); /* We need to pre-buffer */ - if (memblockq_check_prebuf(bq)) + if (pa_memblockq_prebuf_active(bq)) return -1; fix_current_read(bq); @@ -466,13 +471,12 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { length = 0; /* We need to return silence, since no data is yet available */ - if (bq->silence) { - size_t l; - - chunk->memblock = pa_memblock_ref(bq->silence); + if (bq->silence.memblock) { + *chunk = bq->silence; + pa_memblock_ref(chunk->memblock); - l = pa_memblock_get_length(chunk->memblock); - chunk->length = (length <= 0 || length > l) ? l : length; + if (length > 0 && length < chunk->length) + chunk->length = length; } else { @@ -511,7 +515,7 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) { while (length > 0) { /* Do not drop any data when we are in prebuffering mode */ - if (memblockq_check_prebuf(bq)) + if (pa_memblockq_prebuf_active(bq)) break; fix_current_read(bq); @@ -546,10 +550,20 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) { bq->missing += delta; } +void pa_memblockq_rewind(pa_memblockq *bq, size_t length) { + pa_assert(bq); + pa_assert(length % bq->base == 0); + + /* This is kind of the inverse of pa_memblockq_drop() */ + + bq->read_index -= length; + bq->missing -= length; +} + pa_bool_t pa_memblockq_is_readable(pa_memblockq *bq) { pa_assert(bq); - if (memblockq_check_prebuf(bq)) + if (pa_memblockq_prebuf_active(bq)) return FALSE; if (pa_memblockq_get_length(bq) <= 0) @@ -621,10 +635,7 @@ void pa_memblockq_flush(pa_memblockq *bq) { int64_t old, delta; pa_assert(bq); - while (bq->blocks) - drop_block(bq, bq->blocks); - - pa_assert(bq->n_blocks == 0); + pa_memblockq_silence(bq); old = bq->write_index; bq->write_index = bq->read_index; @@ -757,18 +768,17 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) { size_t old_tlength; pa_assert(bq); - old_tlength = bq->tlength; - if (tlength <= 0) tlength = bq->maxlength; + old_tlength = bq->tlength; bq->tlength = ((tlength+bq->base-1)/bq->base)*bq->base; if (bq->tlength > bq->maxlength) bq->tlength = bq->maxlength; - if (bq->minreq > bq->tlength - bq->prebuf) - pa_memblockq_set_minreq(bq, bq->tlength - bq->prebuf); + if (bq->minreq > bq->tlength) + pa_memblockq_set_minreq(bq, bq->tlength); bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength; } @@ -776,8 +786,10 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) { void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) { pa_assert(bq); - bq->prebuf = (prebuf == (size_t) -1) ? bq->tlength : prebuf; - bq->prebuf = ((bq->prebuf+bq->base-1)/bq->base)*bq->base; + if (prebuf == (size_t) -1) + prebuf = bq->tlength; + + bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base; if (prebuf > 0 && bq->prebuf < bq->base) bq->prebuf = bq->base; @@ -788,8 +800,8 @@ void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) { if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf) bq->in_prebuf = FALSE; - if (bq->minreq > bq->tlength - bq->prebuf) - pa_memblockq_set_minreq(bq, bq->tlength - bq->prebuf); + if (bq->minreq > bq->prebuf) + pa_memblockq_set_minreq(bq, bq->prebuf); } void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) { @@ -797,8 +809,11 @@ void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) { bq->minreq = (minreq/bq->base)*bq->base; - if (bq->minreq > bq->tlength - bq->prebuf) - bq->minreq = bq->tlength - bq->prebuf; + if (bq->minreq > bq->tlength) + bq->minreq = bq->tlength; + + if (bq->minreq > bq->prebuf) + bq->minreq = bq->prebuf; if (bq->minreq < bq->base) bq->minreq = bq->base; @@ -810,14 +825,6 @@ void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) { bq->maxrewind = (maxrewind/bq->base)*bq->base; } -void pa_memblockq_rewind(pa_memblockq *bq, size_t length) { - pa_assert(bq); - pa_assert(length % bq->base == 0); - - bq->read_index -= length; - bq->missing -= length; -} - int pa_memblockq_splice(pa_memblockq *bq, pa_memblockq *source) { pa_assert(bq); @@ -859,13 +866,17 @@ void pa_memblockq_willneed(pa_memblockq *bq) { pa_memchunk_will_need(&q->chunk); } -void pa_memblockq_set_silence(pa_memblockq *bq, pa_memblock *silence) { +void pa_memblockq_set_silence(pa_memblockq *bq, pa_memchunk *silence) { pa_assert(bq); - if (bq->silence) - pa_memblock_unref(bq->silence); + if (bq->silence.memblock) + pa_memblock_unref(bq->silence.memblock); - bq->silence = silence ? pa_memblock_ref(silence) : NULL; + if (silence) { + bq->silence = *silence; + pa_memblock_ref(bq->silence.memblock); + } else + pa_memchunk_reset(&bq->silence); } pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq) { @@ -873,3 +884,12 @@ pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq) { return !bq->blocks; } + +void pa_memblockq_silence(pa_memblockq *bq) { + pa_assert(bq); + + while (bq->blocks) + drop_block(bq, bq->blocks); + + pa_assert(bq->n_blocks == 0); +} diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h index 8610a1aa..a6065dea 100644 --- a/src/pulsecore/memblockq.h +++ b/src/pulsecore/memblockq.h @@ -64,7 +64,7 @@ typedef struct pa_memblockq pa_memblockq; - maxrewind: how many bytes of history to keep in the queue - - silence: return this memblock when reading unitialized data + - silence: return this memchunk when reading unitialized data */ pa_memblockq* pa_memblockq_new( int64_t idx, @@ -74,7 +74,7 @@ pa_memblockq* pa_memblockq_new( size_t prebuf, size_t minreq, size_t maxrewind, - pa_memblock *silence); + pa_memchunk *silence); void pa_memblockq_free(pa_memblockq*bq); @@ -152,7 +152,7 @@ void pa_memblockq_set_tlength(pa_memblockq *memblockq, size_t tlength); /* might void pa_memblockq_set_prebuf(pa_memblockq *memblockq, size_t prebuf); /* might modify minreq, too */ void pa_memblockq_set_minreq(pa_memblockq *memblockq, size_t minreq); void pa_memblockq_set_maxrewind(pa_memblockq *memblockq, size_t rewind); /* Set the maximum history size */ -void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memblock *silence); +void pa_memblockq_set_silence(pa_memblockq *memblockq, pa_memchunk *silence); /* Call pa_memchunk_willneed() for every chunk in the queue from the current read pointer to the end */ void pa_memblockq_willneed(pa_memblockq *bq); @@ -162,4 +162,9 @@ void pa_memblockq_willneed(pa_memblockq *bq); * data for the future nor data in the backlog. */ pa_bool_t pa_memblockq_is_empty(pa_memblockq *bq); +void pa_memblockq_silence(pa_memblockq *bq); + +/* Check whether we currently are in prebuf state */ +pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq); + #endif diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 3d435c8b..7b9b8fb8 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -255,17 +255,13 @@ void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) { pa_memblockq_free(u->memblockq); if ((u->memblockq = q)) { - pa_memblock *silence; + pa_memchunk silence; pa_memblockq_set_prebuf(q, 0); - silence = pa_silence_memblock_new( - i->sink->core->mempool, - &i->sample_spec, - i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0); - - pa_memblockq_set_silence(q, silence); - pa_memblock_unref(silence); + pa_sink_input_get_silence(i, &silence); + pa_memblockq_set_silence(q, &silence); + pa_memblock_unref(silence.memblock); pa_memblockq_willneed(q); } diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index fc5aa598..37200db6 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -72,6 +72,7 @@ #define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */ #define DEFAULT_TLENGTH_MSEC 2000 /* 2s */ +#define DEFAULT_PROCESS_MSEC 20 /* 20ms */ #define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC typedef struct connection connection; @@ -104,7 +105,7 @@ typedef struct playback_stream { pa_bool_t drain_request; uint32_t drain_tag; uint32_t syncid; - pa_bool_t underrun; + uint64_t underrun; /* length of underrun */ pa_atomic_t missing; size_t minreq; @@ -208,6 +209,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk static void sink_input_kill_cb(pa_sink_input *i); static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend); static void sink_input_moved_cb(pa_sink_input *i); +static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes); +static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes); + static void send_memblock(connection *c); static void request_bytes(struct playback_stream*s); @@ -471,7 +475,6 @@ static record_stream* record_stream_new( pa_source *source, pa_sample_spec *ss, pa_channel_map *map, - const char *name, uint32_t *maxlength, uint32_t *fragsize, pa_source_output_flags_t flags, @@ -485,7 +488,6 @@ static record_stream* record_stream_new( pa_assert(c); pa_assert(ss); - pa_assert(name); pa_assert(maxlength); pa_assert(p); @@ -523,7 +525,7 @@ static record_stream* record_stream_new( if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) *maxlength = MAX_MEMBLOCKQ_LENGTH; if (*fragsize <= 0) - *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*1000, &source_output->sample_spec); + *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &source_output->sample_spec); if (adjust_latency) { pa_usec_t fragsize_usec; @@ -618,21 +620,14 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, uint32_t l = 0; for (;;) { - int32_t k; - - if ((k = pa_atomic_load(&s->missing)) <= 0) - break; - - l += k; - - if (l < s->minreq) + if ((l = pa_atomic_load(&s->missing)) <= 0) break; - if (pa_atomic_sub(&s->missing, k) <= k) + if (pa_atomic_cmpxchg(&s->missing, l, 0)) break; } - if (l < s->minreq) + if (l <= 0) break; t = pa_tagstruct_new(NULL, 0); @@ -642,7 +637,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, pa_tagstruct_putu32(t, l); pa_pstream_send_tagstruct(s->connection->pstream, t); -/* pa_log("Requesting %u bytes", l); */ +/* pa_log("Requesting %lu bytes", (unsigned long) l); */ break; } @@ -684,7 +679,6 @@ static playback_stream* playback_stream_new( pa_sink *sink, pa_sample_spec *ss, pa_channel_map *map, - const char *name, uint32_t *maxlength, uint32_t *tlength, uint32_t *prebuf, @@ -699,14 +693,15 @@ static playback_stream* playback_stream_new( playback_stream *s, *ssync; pa_sink_input *sink_input; - pa_memblock *silence; + pa_memchunk silence; uint32_t idx; int64_t start_index; pa_sink_input_new_data data; + pa_usec_t tlength_usec, minreq_usec, sink_usec; + size_t frame_size; pa_assert(c); pa_assert(ss); - pa_assert(name); pa_assert(maxlength); pa_assert(tlength); pa_assert(prebuf); @@ -761,10 +756,12 @@ static playback_stream* playback_stream_new( s->connection = c; s->syncid = syncid; s->sink_input = sink_input; - s->underrun = TRUE; + s->underrun = (uint64_t) -1; s->sink_input->parent.process_msg = sink_input_process_msg; s->sink_input->pop = sink_input_pop_cb; + s->sink_input->rewind = sink_input_rewind_cb; + s->sink_input->set_max_rewind = sink_input_set_max_rewind_cb; s->sink_input->kill = sink_input_kill_cb; s->sink_input->moved = sink_input_moved_cb; s->sink_input->suspend = sink_input_suspend_cb; @@ -775,40 +772,69 @@ static playback_stream* playback_stream_new( if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) *maxlength = MAX_MEMBLOCKQ_LENGTH; if (*tlength <= 0) - *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*1000, &sink_input->sample_spec); + *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &sink_input->sample_spec); if (*minreq <= 0) - *minreq = (*tlength*9)/10; - if (*prebuf <= 0) - *prebuf = *tlength; + *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &sink_input->sample_spec); + + frame_size = pa_frame_size(&sink_input->sample_spec); + if (*minreq <= 0) + *minreq = frame_size; + if (*tlength < *minreq+frame_size) + *tlength = *minreq+frame_size; + + tlength_usec = pa_bytes_to_usec(*tlength, &sink_input->sample_spec); + minreq_usec = pa_bytes_to_usec(*minreq, &sink_input->sample_spec); if (adjust_latency) { - pa_usec_t tlength_usec, minreq_usec; - /* So, the user asked us to adjust the latency according to - * the what the sink can provide. Half the latency will be - * spent on the hw buffer, half of it in the async buffer - * queue we maintain for each client. */ + /* So, the user asked us to adjust the latency of the stream + * buffer according to the what the sink can provide. The + * tlength passed in shall be the overall latency. Roughly + * half the latency will be spent on the hw buffer, the other + * half of it in the async buffer queue we maintain for each + * client. In between we'll have a safety space of size + * minreq.*/ + + sink_usec = (tlength_usec-minreq_usec)/2; + + } else { + + /* Ok, the user didn't ask us to adjust the latency, but we + * still need to make sure that the parameters from the user + * do make sense. */ + + sink_usec = tlength_usec - minreq_usec; + } - tlength_usec = pa_bytes_to_usec(*tlength, &sink_input->sample_spec); - minreq_usec = pa_bytes_to_usec(*minreq, &sink_input->sample_spec); + s->sink_latency = pa_sink_input_set_requested_latency(sink_input, sink_usec); - s->sink_latency = pa_sink_input_set_requested_latency(sink_input, tlength_usec/2); + if (adjust_latency) { + /* Ok, we didn't necessarily get what we were asking for, so + * let's subtract from what we asked for for the remaining + * buffer space */ - if (tlength_usec >= s->sink_latency*2) + if (tlength_usec >= s->sink_latency) tlength_usec -= s->sink_latency; - else - tlength_usec = s->sink_latency; + } - if (minreq_usec >= s->sink_latency*2) - minreq_usec -= s->sink_latency; - else - minreq_usec = s->sink_latency; + if (tlength_usec < s->sink_latency + minreq_usec) + tlength_usec = s->sink_latency + minreq_usec; - *tlength = pa_usec_to_bytes(tlength_usec, &sink_input->sample_spec); - *minreq = pa_usec_to_bytes(minreq_usec, &sink_input->sample_spec); + *tlength = pa_usec_to_bytes(tlength_usec, &sink_input->sample_spec); + *minreq = pa_usec_to_bytes(minreq_usec, &sink_input->sample_spec); + + if (*minreq <= 0) { + *minreq = frame_size; + *tlength += frame_size; } - silence = pa_silence_memblock_new(c->protocol->core->mempool, &sink_input->sample_spec, 0); + if (*tlength <= *minreq) + *tlength = *minreq + frame_size; + + if (*prebuf <= 0) + *prebuf = *tlength; + + pa_sink_input_get_silence(sink_input, &silence); s->memblockq = pa_memblockq_new( start_index, @@ -818,9 +844,9 @@ static playback_stream* playback_stream_new( *prebuf, *minreq, 0, - silence); + &silence); - pa_memblock_unref(silence); + pa_memblock_unref(silence.memblock); *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); @@ -924,10 +950,12 @@ static void request_bytes(playback_stream *s) { if (m <= 0) return; -/* pa_log("request_bytes(%u)", m); */ +/* pa_log("request_bytes(%lu)", (unsigned long) m); */ previous_missing = pa_atomic_add(&s->missing, m); - if (previous_missing < s->minreq && previous_missing+m >= s->minreq) { + + if (pa_memblockq_prebuf_active(s->memblockq) || + (previous_missing < s->minreq && previous_missing+m >= s->minreq)) { pa_assert(pa_thread_mq_get()); pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); } @@ -989,6 +1017,45 @@ static void send_record_stream_killed(record_stream *r) { /*** sink input callbacks ***/ +static void handle_seek(playback_stream *s, int64_t indexw) { + playback_stream_assert_ref(s); + +/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->underrun, pa_memblockq_is_readable(s->memblockq)); */ + + if (s->underrun != 0) { + +/* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */ + + if (pa_memblockq_is_readable(s->memblockq)) { + + size_t u = pa_memblockq_get_length(s->memblockq); + + if (u >= s->underrun) + u = s->underrun; + + pa_log("yeah! ready to rock"); + + /* We just ended an underrun, let's ask the sink + * to rewrite */ + s->sink_input->thread_info.ignore_rewind = TRUE; + pa_sink_input_request_rewind(s->sink_input, u, TRUE); + } + + } else { + int64_t indexr; + + indexr = pa_memblockq_get_read_index(s->memblockq); + + if (indexw < indexr) + /* OK, the sink already asked for this data, so + * let's have it usk us again */ + + pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE); + } + + request_bytes(s); +} + /* Called from thread context */ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_sink_input *i = PA_SINK_INPUT(o); @@ -1000,48 +1067,42 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int switch (code) { - case SINK_INPUT_MESSAGE_SEEK: + case SINK_INPUT_MESSAGE_SEEK: { + int64_t windex; + + windex = pa_memblockq_get_write_index(s->memblockq); pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata)); - request_bytes(s); + + handle_seek(s, windex); return 0; + } case SINK_INPUT_MESSAGE_POST_DATA: { + int64_t windex; + pa_assert(chunk); -/* pa_log("sink input post: %u", chunk->length); */ +/* pa_log("sink input post: %lu", (unsigned long) chunk->length); */ - if (pa_memblockq_push_align(s->memblockq, chunk) < 0) { + windex = pa_memblockq_get_write_index(s->memblockq); + if (pa_memblockq_push_align(s->memblockq, chunk) < 0) { pa_log_warn("Failed to push data into queue"); pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL); pa_memblockq_seek(s->memblockq, chunk->length, PA_SEEK_RELATIVE); } - request_bytes(s); - - s->underrun = FALSE; - return 0; - } - - case SINK_INPUT_MESSAGE_DRAIN: { - - pa_memblockq_prebuf_disable(s->memblockq); - - if (!pa_memblockq_is_readable(s->memblockq)) - pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL); - else { - s->drain_tag = PA_PTR_TO_UINT(userdata); - s->drain_request = TRUE; - } - request_bytes(s); + handle_seek(s, windex); return 0; } + case SINK_INPUT_MESSAGE_DRAIN: case SINK_INPUT_MESSAGE_FLUSH: case SINK_INPUT_MESSAGE_PREBUF_FORCE: case SINK_INPUT_MESSAGE_TRIGGER: { + int64_t windex; pa_sink_input *isync; void (*func)(pa_memblockq *bq); @@ -1054,6 +1115,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int func = pa_memblockq_prebuf_force; break; + case SINK_INPUT_MESSAGE_DRAIN: case SINK_INPUT_MESSAGE_TRIGGER: func = pa_memblockq_prebuf_disable; break; @@ -1062,23 +1124,32 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int pa_assert_not_reached(); } + windex = pa_memblockq_get_write_index(s->memblockq); func(s->memblockq); - s->underrun = FALSE; - request_bytes(s); + handle_seek(s, windex); /* Do the same for all other members in the sync group */ for (isync = i->sync_prev; isync; isync = isync->sync_prev) { playback_stream *ssync = PLAYBACK_STREAM(isync->userdata); + windex = pa_memblockq_get_write_index(ssync->memblockq); func(ssync->memblockq); - ssync->underrun = FALSE; - request_bytes(ssync); + handle_seek(ssync, windex); } for (isync = i->sync_next; isync; isync = isync->sync_next) { playback_stream *ssync = PLAYBACK_STREAM(isync->userdata); + windex = pa_memblockq_get_write_index(ssync->memblockq); func(ssync->memblockq); - ssync->underrun = FALSE; - request_bytes(ssync); + handle_seek(ssync, windex); + } + + if (code == SINK_INPUT_MESSAGE_DRAIN) { + if (!pa_memblockq_is_readable(s->memblockq)) + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL); + else { + s->drain_tag = PA_PTR_TO_UINT(userdata); + s->drain_request = TRUE; + } } return 0; @@ -1091,11 +1162,18 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq); return 0; - case PA_SINK_INPUT_MESSAGE_SET_STATE: + case PA_SINK_INPUT_MESSAGE_SET_STATE: { + int64_t windex; + + windex = pa_memblockq_get_write_index(s->memblockq); pa_memblockq_prebuf_force(s->memblockq); - request_bytes(s); + + handle_seek(s, windex); + + /* Fall through to the default handler */ break; + } case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = userdata; @@ -1112,7 +1190,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int } /* Called from thread context */ -static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { playback_stream *s; pa_sink_input_assert_ref(i); @@ -1122,23 +1200,58 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk if (pa_memblockq_peek(s->memblockq, chunk) < 0) { +/* pa_log("UNDERRUN"); */ + if (s->drain_request && pa_sink_input_safe_to_remove(i)) { s->drain_request = FALSE; pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL); - } else if (!s->underrun) { - s->underrun = TRUE; + } else if (s->underrun == 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL); - } + + if (s->underrun != (size_t) -1) + s->underrun += nbytes; + +/* pa_log("added %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) s->underrun); */ + + request_bytes(s); return -1; } +/* pa_log("NOTUNDERRUN"); */ + + s->underrun = 0; + pa_memblockq_drop(s->memblockq, chunk->length); request_bytes(s); return 0; } +static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { + playback_stream *s; + + pa_sink_input_assert_ref(i); + s = PLAYBACK_STREAM(i->userdata); + playback_stream_assert_ref(s); + + /* If we are in an underrun, then we don't rewind */ + if (s->underrun != 0) + return; + + pa_memblockq_rewind(s->memblockq, nbytes); +} + +static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + playback_stream *s; + + pa_sink_input_assert_ref(i); + s = PLAYBACK_STREAM(i->userdata); + playback_stream_assert_ref(s); + + pa_memblockq_set_maxrewind(s->memblockq, nbytes); +} + /* Called from main context */ static void sink_input_kill_cb(pa_sink_input *i) { playback_stream *s; @@ -1416,7 +1529,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0); - s = playback_stream_new(c, sink, &ss, &map, name, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency); + s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, &volume, muted, syncid, &missing, flags, p, adjust_latency); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1625,7 +1738,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); - s = record_stream_new(c, source, &ss, &map, name, &maxlength, &fragment_size, flags, p, adjust_latency); + s = record_stream_new(c, source, &ss, &map, &maxlength, &fragment_size, flags, p, adjust_latency); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -1721,7 +1834,6 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t pa_log_warn("failed to get GID of group '%s'", c->protocol->auth_group); else if (gid == creds->gid) success = 1; - if (!success) { if ((r = pa_uid_in_group(creds->uid, c->protocol->auth_group)) < 0) pa_log_warn("failed to check group membership."); @@ -1739,7 +1851,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t pa_mempool_is_shared(c->protocol->core->mempool) && creds->uid == getuid()) { - pa_pstream_use_shm(c->pstream, 1); + pa_pstream_enable_shm(c->pstream, TRUE); pa_log_info("Enabled SHM for new connection"); } diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 01e3bd9c..7e6c5de1 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -42,7 +42,7 @@ #include "sink-input.h" -#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) +#define MEMBLOCKQ_MAXLENGTH (32*1024*1024) #define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE) #define MOVE_BUFFER_LENGTH (PA_PAGE_SIZE*256) @@ -94,6 +94,20 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data) { pa_proplist_free(data->proplist); } +static void reset_callbacks(pa_sink_input *i) { + pa_assert(i); + + i->pop = NULL; + i->rewind = NULL; + i->set_max_rewind = NULL; + i->attach = NULL; + i->detach = NULL; + i->suspend = NULL; + i->moved = NULL; + i->kill = NULL; + i->get_latency = NULL; +} + pa_sink_input* pa_sink_input_new( pa_core *core, pa_sink_input_new_data *data, @@ -102,7 +116,6 @@ pa_sink_input* pa_sink_input_new( pa_sink_input *i; pa_resampler *resampler = NULL; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; - pa_memblock *silence; pa_assert(core); pa_assert(data); @@ -223,15 +236,7 @@ pa_sink_input* pa_sink_input_new( } else i->sync_next = i->sync_prev = NULL; - i->pop = NULL; - i->rewind = NULL; - i->set_max_rewind = NULL; - i->kill = NULL; - i->get_latency = NULL; - i->attach = NULL; - i->detach = NULL; - i->suspend = NULL; - i->moved = NULL; + reset_callbacks(i); i->userdata = NULL; i->thread_info.state = i->state; @@ -244,10 +249,9 @@ pa_sink_input* pa_sink_input_new( i->thread_info.muted = i->muted; i->thread_info.requested_sink_latency = 0; i->thread_info.rewrite_nbytes = 0; + i->thread_info.since_underrun = 0; i->thread_info.ignore_rewind = FALSE; - silence = pa_silence_memblock_new(i->sink->core->mempool, &i->sink->sample_spec, 0); - i->thread_info.render_memblockq = pa_memblockq_new( 0, MEMBLOCKQ_MAXLENGTH, @@ -256,9 +260,7 @@ pa_sink_input* pa_sink_input_new( 0, 1, 0, - silence); - - pa_memblock_unref(silence); + &i->sink->silence); pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0); pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0); @@ -349,15 +351,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { } else i->state = PA_SINK_INPUT_UNLINKED; - i->pop = NULL; - i->rewind = NULL; - i->set_max_rewind = NULL; - i->kill = NULL; - i->get_latency = NULL; - i->attach = NULL; - i->detach = NULL; - i->suspend = NULL; - i->moved = NULL; + reset_callbacks(i); if (linked) { pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index); @@ -395,19 +389,21 @@ static void sink_input_free(pa_object *o) { } void pa_sink_input_put(pa_sink_input *i) { + pa_sink_input_state_t state; pa_sink_input_assert_ref(i); pa_assert(i->state == PA_SINK_INPUT_INIT); pa_assert(i->pop); + pa_assert(i->rewind); - i->thread_info.state = i->state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; i->thread_info.volume = i->volume; i->thread_info.muted = i->muted; - if (i->state == PA_SINK_INPUT_CORKED) - i->sink->n_corked++; + state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; + + update_n_corked(i, state); + i->thread_info.state = i->state = state; - pa_sink_update_status(i->sink); pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); @@ -454,18 +450,20 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_assert(chunk); pa_assert(volume); - pa_log_debug("peek"); +/* pa_log_debug("peek"); */ - if (!i->pop || i->thread_info.state == PA_SINK_INPUT_CORKED) + if (!i->pop) return -1; - pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING || i->thread_info.state == PA_SINK_INPUT_DRAINED); + pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING || + i->thread_info.state == PA_SINK_INPUT_CORKED || + i->thread_info.state == PA_SINK_INPUT_DRAINED); /* If there's still some rewrite request the handle, but the sink didn't do this for us, we do it here. However, since the sink apparently doesn't support rewinding, we pass 0 here. This still allows rewinding through the render buffer. */ - pa_sink_input_rewind(i, 0); + pa_sink_input_process_rewind(i, 0); block_size_max_sink_input = i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : @@ -504,11 +502,15 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa /* There's nothing in our render queue. We need to fill it up * with data from the implementor. */ - if (i->pop(i, ilength, &tchunk) < 0) { + if (i->thread_info.state == PA_SINK_INPUT_CORKED || + i->pop(i, ilength, &tchunk) < 0) { + + /* OK, we're corked or the implementor didn't give us any + * data, so let's just hand out silence */ pa_atomic_store(&i->thread_info.drained, 1); - /* OK, we got no data from the implementor, so let's just skip ahead */ pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ); + i->thread_info.since_underrun = 0; break; } @@ -517,10 +519,13 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_assert(tchunk.length > 0); pa_assert(tchunk.memblock); + i->thread_info.since_underrun += tchunk.length; + while (tchunk.length > 0) { pa_memchunk wchunk; wchunk = tchunk; + pa_memblock_ref(wchunk.memblock); if (wchunk.length > block_size_max_sink_input) wchunk.length = block_size_max_sink_input; @@ -529,6 +534,8 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa if (do_volume_adj_here && !volume_is_norm) { pa_memchunk_make_writable(&wchunk, 0); + pa_log_debug("adjusting volume!"); + if (i->thread_info.muted) pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec); else @@ -547,6 +554,8 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa } } + pa_memblock_unref(wchunk.memblock); + tchunk.index += wchunk.length; tchunk.length -= wchunk.length; } @@ -581,22 +590,18 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa /* Called from thread context */ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { - pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); pa_assert(nbytes > 0); - if (i->thread_info.state == PA_SINK_INPUT_CORKED) - return; - /* If there's still some rewrite request the handle, but the sink didn't do this for us, we do it here. However, since the sink apparently doesn't support rewinding, we pass 0 here. This still allows rewinding through the render buffer. */ if (i->thread_info.rewrite_nbytes > 0) - pa_sink_input_rewind(i, 0); + pa_sink_input_process_rewind(i, 0); pa_memblockq_drop(i->thread_info.render_memblockq, nbytes); @@ -604,25 +609,22 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec * } /* Called from thread context */ -void pa_sink_input_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { +void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); - pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); - - if (i->thread_info.state == PA_SINK_INPUT_CORKED) - return; +/* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */ if (i->thread_info.ignore_rewind) { - i->thread_info.rewrite_nbytes = 0; i->thread_info.ignore_rewind = FALSE; + i->thread_info.rewrite_nbytes = 0; return; } if (nbytes > 0) - pa_log_debug("Have to rewind %lu bytes.", (unsigned long) nbytes); + pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); if (i->thread_info.rewrite_nbytes > 0) { size_t max_rewrite; @@ -641,19 +643,23 @@ void pa_sink_input_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec /* Convert back to to sink domain */ r = i->thread_info.resampler ? pa_resampler_result(i->thread_info.resampler, amount) : amount; - /* Ok, now update the write pointer */ - pa_memblockq_seek(i->thread_info.render_memblockq, -r, PA_SEEK_RELATIVE); + if (r > 0) + /* Ok, now update the write pointer */ + pa_memblockq_seek(i->thread_info.render_memblockq, -r, PA_SEEK_RELATIVE); + + if (amount) { + pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); - /* Tell the implementor */ - if (i->rewind) - i->rewind(i, amount); + /* Tell the implementor */ + if (i->rewind) + i->rewind(i, amount); + } /* And reset the resampler */ if (i->thread_info.resampler) pa_resampler_reset(i->thread_info.resampler); } - i->thread_info.rewrite_nbytes = 0; } @@ -664,7 +670,6 @@ void pa_sink_input_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec /* Called from thread context */ void pa_sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); @@ -935,7 +940,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { /* Replace resampler */ if (new_resampler != i->thread_info.resampler) { - pa_memblock *silence; + pa_memchunk silence; if (i->thread_info.resampler) pa_resampler_free(i->thread_info.resampler); @@ -944,9 +949,15 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { /* if the resampler changed, the silence memblock is * probably invalid now, too */ - silence = pa_silence_memblock_new(i->sink->core->mempool, &dest->sample_spec, new_resampler ? pa_resampler_max_block_size(new_resampler) : 0); - pa_memblockq_set_silence(i->thread_info.render_memblockq, silence); - pa_memblock_unref(silence); + pa_silence_memchunk_get( + &i->sink->core->silence_cache, + i->sink->core->mempool, + &silence, + &dest->sample_spec, + 0); + + pa_memblockq_set_silence(i->thread_info.render_memblockq, &silence); + pa_memblock_unref(silence.memblock); } @@ -974,6 +985,35 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { return 0; } +static void set_state(pa_sink_input *i, pa_sink_input_state_t state) { + pa_sink_input_assert_ref(i); + + if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) && + !(i->thread_info.state == PA_SINK_INPUT_DRAINED || i->thread_info.state != PA_SINK_INPUT_RUNNING)) + pa_atomic_store(&i->thread_info.drained, 1); + + if (state == PA_SINK_INPUT_CORKED && i->thread_info.state != PA_SINK_INPUT_CORKED) { + + /* OK, we're corked, so let's make sure we have total silence + * from now on on this stream */ + pa_memblockq_silence(i->thread_info.render_memblockq); + + /* This will tell the implementing sink input driver to rewind + * so that the unplayed already mixed data is not lost */ + pa_sink_input_request_rewind(i, 0, FALSE); + + } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) { + + /* OK, we're being uncorked. Make sure we're not rewound when + * the hw buffer is remixed and request a remix. */ + i->thread_info.ignore_rewind = TRUE; + i->thread_info.since_underrun = 0; + pa_sink_request_rewind(i->sink, 0); + } + + i->thread_info.state = state; +} + /* Called from thread context */ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_sink_input *i = PA_SINK_INPUT(o); @@ -984,12 +1024,12 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t switch (code) { case PA_SINK_INPUT_MESSAGE_SET_VOLUME: i->thread_info.volume = *((pa_cvolume*) userdata); - pa_sink_input_request_rewrite(i, 0); + pa_sink_input_request_rewind(i, 0, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_SET_MUTE: i->thread_info.muted = PA_PTR_TO_UINT(userdata); - pa_sink_input_request_rewrite(i, 0); + pa_sink_input_request_rewind(i, 0, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { @@ -1010,25 +1050,13 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t case PA_SINK_INPUT_MESSAGE_SET_STATE: { pa_sink_input *ssync; - if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) && - (i->thread_info.state != PA_SINK_INPUT_DRAINED) && (i->thread_info.state != PA_SINK_INPUT_RUNNING)) - pa_atomic_store(&i->thread_info.drained, 1); + set_state(i, PA_PTR_TO_UINT(userdata)); - i->thread_info.state = PA_PTR_TO_UINT(userdata); + for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev) + set_state(ssync, PA_PTR_TO_UINT(userdata)); - for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev) { - if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) && - (ssync->thread_info.state != PA_SINK_INPUT_DRAINED) && (ssync->thread_info.state != PA_SINK_INPUT_RUNNING)) - pa_atomic_store(&ssync->thread_info.drained, 1); - ssync->thread_info.state = PA_PTR_TO_UINT(userdata); - } - - for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next) { - if ((PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_DRAINED || PA_PTR_TO_UINT(userdata) == PA_SINK_INPUT_RUNNING) && - (ssync->thread_info.state != PA_SINK_INPUT_DRAINED) && (ssync->thread_info.state != PA_SINK_INPUT_RUNNING)) - pa_atomic_store(&ssync->thread_info.drained, 1); - ssync->thread_info.state = PA_PTR_TO_UINT(userdata); - } + for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next) + set_state(ssync, PA_PTR_TO_UINT(userdata)); return 0; } @@ -1062,24 +1090,42 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { return TRUE; } -void pa_sink_input_request_rewrite(pa_sink_input *i, size_t nbytes /* in our sample spec */) { +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns) { size_t l, lbq; pa_sink_input_assert_ref(i); + /* We don't take rewind requests while we are corked */ + if (i->state == PA_SINK_INPUT_CORKED) + return; + lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); if (nbytes <= 0) { + /* Calulate maximum number of bytes that could be rewound in theory */ + nbytes = i->sink->thread_info.max_rewind + lbq; + + /* Transform from sink domain */ nbytes = i->thread_info.resampler ? - pa_resampler_request(i->thread_info.resampler, i->sink->thread_info.max_rewind + lbq) : - (i->sink->thread_info.max_rewind + lbq); + pa_resampler_request(i->thread_info.resampler, nbytes) : + nbytes; } - i->thread_info.rewrite_nbytes = PA_MAX(nbytes, i->thread_info.rewrite_nbytes); + /* Increase the number of bytes to rewrite, never decrease */ + if (nbytes > i->thread_info.rewrite_nbytes) + i->thread_info.rewrite_nbytes = nbytes; + + if (!ignore_underruns) { + /* Make sure to not overwrite over underruns */ + if ((int64_t) i->thread_info.rewrite_nbytes > i->thread_info.since_underrun) + i->thread_info.rewrite_nbytes = (size_t) i->thread_info.since_underrun; + } /* Transform to sink domain */ - l = i->thread_info.resampler ? pa_resampler_result(i->thread_info.resampler, nbytes) : nbytes; + l = i->thread_info.resampler ? + pa_resampler_result(i->thread_info.resampler, i->thread_info.rewrite_nbytes) : + i->thread_info.rewrite_nbytes; if (l <= 0) return; @@ -1087,3 +1133,17 @@ void pa_sink_input_request_rewrite(pa_sink_input *i, size_t nbytes /* in our sa if (l > lbq) pa_sink_request_rewind(i->sink, l - lbq); } + +pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { + pa_sink_input_assert_ref(i); + pa_assert(ret); + + pa_silence_memchunk_get( + &i->sink->core->silence_cache, + i->sink->core->mempool, + ret, + &i->sample_spec, + i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0); + + return ret; +} diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 8545dea3..184a2c4e 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -97,16 +97,16 @@ struct pa_sink_input { * only. If less data is available, it's fine to return a smaller * block. If more data is already ready, it is better to return * the full block. */ - int (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); + int (*pop) (pa_sink_input *i, size_t request_nbytes, pa_memchunk *chunk); /* may NOT be NULL */ /* Rewind the queue by the specified number of bytes. Called just * before peek() if it is called at all. Only called if the sink * input driver ever plans to call - * pa_sink_input_request_rewrite(). Called from IO context. */ - void (*rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ + * pa_sink_input_request_rewind(). Called from IO context. */ + void (*rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */ /* Called whenever the maximum rewindable size of the sink - * changes. Called from UI context. */ + * changes. Called from RT context. */ void (*set_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ /* If non-NULL this function is called when the input is first @@ -150,7 +150,9 @@ struct pa_sink_input { /* We maintain a history of resampled audio data here. */ pa_memblockq *render_memblockq; + size_t rewrite_nbytes; + int64_t since_underrun; pa_bool_t ignore_rewind; pa_sink_input *sync_prev, *sync_next; @@ -229,14 +231,13 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name); pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec); /* Request that the specified number of bytes already written out to -the hw device is rewritten, if possible. If this function is used you -need to supply the ->rewind() function pointer. Please note that this -is only a kind request. The sink driver may not be able to fulfill it +the hw device is rewritten, if possible. Please note that this is +only a kind request. The sink driver may not be able to fulfill it fully -- or at all. If the request for a rewrite was successful, the sink driver will call ->rewind() and pass the number of bytes that could be rewound in the HW device. This functionality is required for implementing the "zero latency" write-through functionality. */ -void pa_sink_input_request_rewrite(pa_sink_input *i, size_t nbytes); +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind); /* Callable by everyone from main thread*/ @@ -265,7 +266,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i); int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume); void pa_sink_input_drop(pa_sink_input *i, size_t length); -void pa_sink_input_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); +void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); void pa_sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); @@ -277,5 +278,6 @@ typedef struct pa_sink_input_move_info { size_t buffer_bytes; } pa_sink_input_move_info; +pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret); #endif diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 6dcc7ab8..f631de35 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -105,6 +105,19 @@ void pa_sink_new_data_done(pa_sink_new_data *data) { pa_proplist_free(data->proplist); } +static void reset_callbacks(pa_sink *s) { + pa_assert(s); + + s->set_state = NULL; + s->get_volume = NULL; + s->set_volume = NULL; + s->get_mute = NULL; + s->set_mute = NULL; + s->get_latency = NULL; + s->request_rewind = NULL; + s->update_requested_latency = NULL; +} + pa_sink* pa_sink_new( pa_core *core, pa_sink_new_data *data, @@ -182,19 +195,18 @@ pa_sink* pa_sink_new( s->muted = data->muted; s->refresh_volume = s->refresh_mute = FALSE; - s->get_latency = NULL; - s->set_volume = NULL; - s->get_volume = NULL; - s->set_mute = NULL; - s->get_mute = NULL; - s->set_state = NULL; - s->request_rewind = NULL; - s->update_requested_latency = NULL; + reset_callbacks(s); s->userdata = NULL; s->asyncmsgq = NULL; s->rtpoll = NULL; - s->silence = pa_silence_memblock_new(core->mempool, &s->sample_spec, 0); + + pa_silence_memchunk_get( + &core->silence_cache, + core->mempool, + &s->silence, + &s->sample_spec, + 0); s->min_latency = DEFAULT_MIN_LATENCY; s->max_latency = s->min_latency; @@ -239,6 +251,7 @@ pa_sink* pa_sink_new( } s->monitor_source->monitor_of = s; + pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind); return s; } @@ -336,14 +349,7 @@ void pa_sink_unlink(pa_sink* s) { else s->state = PA_SINK_UNLINKED; - s->get_latency = NULL; - s->get_volume = NULL; - s->set_volume = NULL; - s->set_mute = NULL; - s->get_mute = NULL; - s->set_state = NULL; - s->request_rewind = NULL; - s->update_requested_latency = NULL; + reset_callbacks(s); if (s->monitor_source) pa_source_unlink(s->monitor_source); @@ -378,8 +384,8 @@ static void sink_free(pa_object *o) { pa_hashmap_free(s->thread_info.inputs, NULL, NULL); - if (s->silence) - pa_memblock_unref(s->silence); + if (s->silence.memblock) + pa_memblock_unref(s->silence.memblock); pa_xfree(s->name); pa_xfree(s->driver); @@ -429,37 +435,31 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE); } -void pa_sink_ping(pa_sink *s) { - pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); - - pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_PING, NULL, 0, NULL, NULL); -} - -void pa_sink_process_rewind(pa_sink *s) { +void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_sink_input *i; void *state = NULL; pa_sink_assert_ref(s); pa_assert(PA_SINK_LINKED(s->state)); - if (s->thread_info.rewind_nbytes <= 0) + if (nbytes <= 0) return; pa_log_debug("Processing rewind..."); while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { pa_sink_input_assert_ref(i); - - pa_sink_input_rewind(i, s->thread_info.rewind_nbytes); + pa_sink_input_process_rewind(i, nbytes); } - s->thread_info.rewind_nbytes = 0; + if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) + pa_source_process_rewind(s->monitor_source, nbytes); } -static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsigned maxinfo) { +static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) { pa_sink_input *i; unsigned n = 0; void *state = NULL; + size_t mixlength = *length; pa_sink_assert_ref(s); pa_assert(info); @@ -467,9 +467,12 @@ static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsi while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) { pa_sink_input_assert_ref(i); - if (pa_sink_input_peek(i, length, &info->chunk, &info->volume) < 0) + if (pa_sink_input_peek(i, *length, &info->chunk, &info->volume) < 0) continue; + if (mixlength == 0 || info->chunk.length < mixlength) + mixlength = info->chunk.length; + if (pa_memblock_is_silence(info->chunk.memblock)) { pa_memblock_unref(info->chunk.memblock); continue; @@ -485,6 +488,9 @@ static unsigned fill_mix_info(pa_sink *s, size_t length, pa_mix_info *info, unsi maxinfo--; } + if (mixlength > 0) + *length = mixlength; + return n; } @@ -569,13 +575,15 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_assert(length > 0); - n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, length, info, MAX_MIX_CHANNELS) : 0; + n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0; if (n == 0) { - result->memblock = pa_memblock_ref(s->silence); - result->length = PA_MIN(pa_memblock_get_length(s->silence), length); - result->index = 0; + *result = s->silence; + pa_memblock_ref(result->memblock); + + if (result->length > length) + result->length = length; } else if (n == 1) { pa_cvolume volume; @@ -589,6 +597,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&volume)) { + pa_log("adjusting volume "); pa_memchunk_make_writable(result, 0); if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) pa_silence_memchunk(result, &s->sample_spec); @@ -600,7 +609,11 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { result->memblock = pa_memblock_new(s->core->mempool, length); ptr = pa_memblock_acquire(result->memblock); - result->length = pa_mix(info, n, ptr, length, &s->sample_spec, &s->thread_info.soft_volume, s->thread_info.soft_muted); + result->length = pa_mix(info, n, + ptr, length, + &s->sample_spec, + &s->thread_info.soft_volume, + s->thread_info.soft_muted); pa_memblock_release(result->memblock); result->index = 0; @@ -618,6 +631,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { pa_mix_info info[MAX_MIX_CHANNELS]; unsigned n; + size_t length, block_size_max; pa_sink_assert_ref(s); pa_assert(PA_SINK_OPENED(s->thread_info.state)); @@ -630,34 +644,44 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { s->thread_info.rewind_nbytes = 0; - n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, target->length, info, MAX_MIX_CHANNELS) : 0; + length = target->length; + block_size_max = pa_mempool_block_size_max(s->core->mempool); + if (length > block_size_max) + length = pa_frame_align(block_size_max, &s->sample_spec); + + n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0; + + if (n == 0) { + if (target->length > length) + target->length = length; - if (n == 0) pa_silence_memchunk(target, &s->sample_spec); - else if (n == 1) { - if (target->length > info[0].chunk.length) - target->length = info[0].chunk.length; + } else if (n == 1) { + pa_cvolume volume; + + if (target->length > length) + target->length = length; + + pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); - if (s->thread_info.soft_muted) + if (s->thread_info.soft_muted || pa_cvolume_is_muted(&volume)) pa_silence_memchunk(target, &s->sample_spec); else { - void *src, *ptr; - pa_cvolume volume; + pa_memchunk vchunk; - ptr = pa_memblock_acquire(target->memblock); - src = pa_memblock_acquire(info[0].chunk.memblock); + vchunk = info[0].chunk; + pa_memblock_ref(vchunk.memblock); - memcpy((uint8_t*) ptr + target->index, - (uint8_t*) src + info[0].chunk.index, - target->length); + if (vchunk.length > target->length) + vchunk.length = target->length; - pa_memblock_release(target->memblock); - pa_memblock_release(info[0].chunk.memblock); - - pa_sw_cvolume_multiply(&volume, &s->thread_info.soft_volume, &info[0].volume); + if (!pa_cvolume_is_norm(&volume)) { + pa_memchunk_make_writable(&vchunk, 0); + pa_volume_memchunk(&vchunk, &s->sample_spec, &volume); + } - if (!pa_cvolume_is_norm(&volume)) - pa_volume_memchunk(target, &s->sample_spec, &volume); + pa_memchunk_memcpy(target, &vchunk); + pa_memblock_unref(vchunk.memblock); } } else { @@ -666,8 +690,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { ptr = pa_memblock_acquire(target->memblock); target->length = pa_mix(info, n, - (uint8_t*) ptr + target->index, - target->length, + (uint8_t*) ptr + target->index, length, &s->sample_spec, &s->thread_info.soft_volume, s->thread_info.soft_muted); @@ -923,12 +946,11 @@ unsigned pa_sink_used_by(pa_sink *s) { ret = pa_idxset_size(s->inputs); pa_assert(ret >= s->n_corked); - ret -= s->n_corked; /* Streams connected to our monitor source do not matter for * pa_sink_used_by()!.*/ - return ret; + return ret - s->n_corked; } int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { @@ -973,7 +995,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_invalidate_requested_latency(s); + /* Make sure we're not rewound when the hw buffer is remixed and request a remix*/ i->thread_info.ignore_rewind = TRUE; + i->thread_info.since_underrun = 0; pa_sink_request_rewind(s, 0); return 0; @@ -1124,9 +1148,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse *((pa_bool_t*) userdata) = s->thread_info.soft_muted; return 0; - case PA_SINK_MESSAGE_PING: - return 0; - case PA_SINK_MESSAGE_SET_STATE: s->thread_info.state = PA_PTR_TO_UINT(userdata); @@ -1286,7 +1307,6 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); if (max_rewind == s->thread_info.max_rewind) return; @@ -1295,6 +1315,9 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) pa_sink_input_set_max_rewind(i, s->thread_info.max_rewind); + + if (s->monitor_source) + pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind); } void pa_sink_invalidate_requested_latency(pa_sink *s) { diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 2dfd8452..97491f43 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -89,14 +89,14 @@ struct pa_sink { pa_asyncmsgq *asyncmsgq; pa_rtpoll *rtpoll; - pa_memblock *silence; + pa_memchunk silence; pa_usec_t min_latency; /* we won't go below this latency */ pa_usec_t max_latency; /* An upper limit for the latencies */ int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */ - int (*set_volume)(pa_sink *s); /* dito */ int (*get_volume)(pa_sink *s); /* dito */ + int (*set_volume)(pa_sink *s); /* dito */ int (*get_mute)(pa_sink *s); /* dito */ int (*set_mute)(pa_sink *s); /* dito */ pa_usec_t (*get_latency)(pa_sink *s); /* dito */ @@ -138,7 +138,6 @@ typedef enum pa_sink_message { PA_SINK_MESSAGE_GET_LATENCY, PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, PA_SINK_MESSAGE_SET_STATE, - PA_SINK_MESSAGE_PING, PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, PA_SINK_MESSAGE_ATTACH, PA_SINK_MESSAGE_DETACH, @@ -199,13 +198,6 @@ int pa_sink_update_status(pa_sink*s); int pa_sink_suspend(pa_sink *s, pa_bool_t suspend); int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend); -void pa_sink_rewind(pa_sink *s, size_t length); - -/* Sends a ping message to the sink thread, to make it wake up and - * check for data to process even if there is no real message is - * sent */ -void pa_sink_ping(pa_sink *s); - void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume); const pa_cvolume *pa_sink_get_volume(pa_sink *sink); void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute); @@ -217,14 +209,14 @@ unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are n /* To be called exclusively by the sink driver, from IO context */ -void pa_sink_process_rewind(pa_sink *s); - void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); void pa_sink_render_into(pa_sink*s, pa_memchunk *target); void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); void pa_sink_skip(pa_sink *s, size_t length); +void pa_sink_process_rewind(pa_sink *s, size_t nbytes); + int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); void pa_sink_attach_within_thread(pa_sink *s); diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 7e0f8ef1..9030d6db 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -235,7 +235,7 @@ int pa_play_file( pa_sample_spec ss; pa_sink_input_new_data data; int fd; - pa_memblock *silence; + pa_memchunk silence; pa_assert(sink); pa_assert(fname); @@ -336,13 +336,9 @@ int pa_play_file( u->sink_input->kill = sink_input_kill_cb; u->sink_input->userdata = u; - silence = pa_silence_memblock_new( - u->core->mempool, - &u->sink_input->sample_spec, - u->sink_input->thread_info.resampler ? pa_resampler_max_block_size(u->sink_input->thread_info.resampler) : 0); - - u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&u->sink_input->sample_spec), 1, 1, 0, silence); - pa_memblock_unref(silence); + pa_sink_input_get_silence(u->sink_input, &silence); + u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&u->sink_input->sample_spec), 1, 1, 0, &silence); + pa_memblock_unref(silence.memblock); pa_sink_input_put(u->sink_input); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index c0cbb42f..318f6f84 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -40,6 +40,8 @@ #include "source-output.h" +#define MEMBLOCKQ_MAXLENGTH (32*1024*1024) + static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject); static void source_output_free(pa_object* mo); @@ -74,6 +76,20 @@ void pa_source_output_new_data_done(pa_source_output_new_data *data) { pa_proplist_free(data->proplist); } +static void reset_callbacks(pa_source_output *o) { + pa_assert(o); + + o->push = NULL; + o->rewind = NULL; + o->set_max_rewind = NULL; + o->attach = NULL; + o->detach = NULL; + o->suspend = NULL; + o->moved = NULL; + o->kill = NULL; + o->get_latency = NULL; +} + pa_source_output* pa_source_output_new( pa_core *core, pa_source_output_new_data *data, @@ -175,13 +191,7 @@ pa_source_output* pa_source_output_new( o->sample_spec = data->sample_spec; o->channel_map = data->channel_map; - o->push = NULL; - o->kill = NULL; - o->get_latency = NULL; - o->detach = NULL; - o->attach = NULL; - o->suspend = NULL; - o->moved = NULL; + reset_callbacks(o); o->userdata = NULL; o->thread_info.state = o->state; @@ -190,6 +200,16 @@ pa_source_output* pa_source_output_new( o->thread_info.resampler = resampler; o->thread_info.requested_source_latency = 0; + o->thread_info.delay_memblockq = pa_memblockq_new( + 0, + MEMBLOCKQ_MAXLENGTH, + 0, + pa_frame_size(&o->source->sample_spec), + 0, + 1, + 0, + &o->source->silence); + pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); @@ -205,6 +225,17 @@ pa_source_output* pa_source_output_new( return o; } +static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) { + pa_assert(o); + + if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED) + pa_assert_se(o->source->n_corked -- >= 1); + else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED) + o->source->n_corked++; + + pa_source_update_status(o->source); +} + static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); @@ -214,12 +245,7 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) return -1; - if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED) - pa_assert_se(o->source->n_corked -- >= 1); - else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED) - o->source->n_corked++; - - pa_source_update_status(o->source); + update_n_corked(o, state); o->state = state; if (state != PA_SOURCE_OUTPUT_UNLINKED) @@ -253,13 +279,7 @@ void pa_source_output_unlink(pa_source_output*o) { } else o->state = PA_SOURCE_OUTPUT_UNLINKED; - o->push = NULL; - o->kill = NULL; - o->get_latency = NULL; - o->attach = NULL; - o->detach = NULL; - o->suspend = NULL; - o->moved = NULL; + reset_callbacks(o); if (linked) { pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); @@ -282,6 +302,9 @@ static void source_output_free(pa_object* mo) { pa_assert(!o->thread_info.attached); + if (o->thread_info.delay_memblockq) + pa_memblockq_free(o->thread_info.delay_memblockq); + if (o->thread_info.resampler) pa_resampler_free(o->thread_info.resampler); @@ -293,17 +316,17 @@ static void source_output_free(pa_object* mo) { } void pa_source_output_put(pa_source_output *o) { + pa_source_output_state_t state; pa_source_output_assert_ref(o); pa_assert(o->state == PA_SOURCE_OUTPUT_INIT); pa_assert(o->push); - o->thread_info.state = o->state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; + state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; - if (o->state == PA_SOURCE_OUTPUT_CORKED) - o->source->n_corked++; + update_n_corked(o, state); + o->thread_info.state = o->state = state; - pa_source_update_status(o->source); pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); @@ -335,7 +358,8 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o) { /* Called from thread context */ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { - pa_memchunk rchunk; + size_t length; + size_t limit; pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); @@ -347,23 +371,83 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_assert(o->state == PA_SOURCE_OUTPUT_RUNNING); - if (!o->thread_info.resampler) { - o->push(o, chunk); - return; + if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) { + pa_log_debug("Delay queue overflow!"); + pa_memblockq_seek(o->thread_info.delay_memblockq, chunk->length, PA_SEEK_RELATIVE); + } + + limit = o->rewind ? 0 : o->source->thread_info.max_rewind; + + /* Implement the delay queue */ + while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) { + pa_memchunk qchunk; + + length -= limit; + + pa_assert_se(pa_memblockq_peek(o->thread_info.delay_memblockq, &qchunk) >= 0); + + if (qchunk.length > length) + qchunk.length = length; + + pa_assert(qchunk.length > 0); + + if (!o->thread_info.resampler) + o->push(o, &qchunk); + else { + pa_memchunk rchunk; + + pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk); + + if (rchunk.length > 0) + o->push(o, &rchunk); + + pa_memblock_unref(rchunk.memblock); + } + + pa_memblock_unref(qchunk.memblock); } +} + +/* Called from thread context */ +void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) { + pa_source_output_assert_ref(o); + + pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); - pa_resampler_run(o->thread_info.resampler, chunk, &rchunk); - if (!rchunk.length) + if (nbytes <= 0) return; - pa_assert(rchunk.memblock); - o->push(o, &rchunk); - pa_memblock_unref(rchunk.memblock); + if (o->rewind) { + pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0); + + if (o->thread_info.resampler) + nbytes = pa_resampler_result(o->thread_info.resampler, nbytes); + + pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes); + + if (nbytes > 0) + o->rewind(o, nbytes); + + if (o->thread_info.resampler) + pa_resampler_reset(o->thread_info.resampler); + + } else + pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes); +} + +/* Called from thread context */ +void pa_source_output_set_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) { + pa_source_output_assert_ref(o); + pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); + pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); + + if (o->set_max_rewind) + o->set_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes); } pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); if (usec > 0) { @@ -372,7 +456,6 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t if (o->source->min_latency > 0 && usec < o->source->min_latency) usec = o->source->min_latency; - } if (PA_SOURCE_OUTPUT_LINKED(o->state)) @@ -535,6 +618,14 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int switch (code) { + case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: { + pa_usec_t *r = userdata; + + *r += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec); + + return 0; + } + case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: { o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata); diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 0a2286b9..f79761ae 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -82,6 +82,14 @@ struct pa_source_output { * context. */ void (*push)(pa_source_output *o, const pa_memchunk *chunk); + /* Only relevant for monitor sources right now: called when the + * recorded stream is rewound. */ + void (*rewind)(pa_source_output *o, size_t nbytes); + + /* Called whenever the maximum rewindable size of the source + * changes. Called from RT context. */ + void (*set_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */ + /* If non-NULL this function is called when the output is first * connected to a source. Called from IO thread context */ void (*attach) (pa_source_output *o); /* may be NULL */ @@ -117,6 +125,10 @@ struct pa_source_output { pa_resampler* resampler; /* may be NULL */ + /* We maintain a delay memblockq here for source outputs that + * don't implement rewind() */ + pa_memblockq *delay_memblockq; + /* The requested latency for the source */ pa_usec_t requested_source_latency; } thread_info; @@ -196,6 +208,8 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest); /* To be used exclusively by the source driver thread */ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); +void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes); +void pa_source_output_set_max_rewind(pa_source_output *o, size_t nbytes); int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 4f963325..0de78f41 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -99,6 +99,18 @@ void pa_source_new_data_done(pa_source_new_data *data) { pa_proplist_free(data->proplist); } +static void reset_callbacks(pa_source *s) { + pa_assert(s); + + s->set_state = NULL; + s->get_volume = NULL; + s->set_volume = NULL; + s->get_mute = NULL; + s->set_mute = NULL; + s->get_latency = NULL; + s->update_requested_latency = NULL; +} + pa_source* pa_source_new( pa_core *core, pa_source_new_data *data, @@ -171,25 +183,27 @@ pa_source* pa_source_new( s->muted = data->muted; s->refresh_volume = s->refresh_muted = FALSE; - s->min_latency = DEFAULT_MIN_LATENCY; - s->max_latency = s->min_latency; - - s->get_latency = NULL; - s->set_volume = NULL; - s->get_volume = NULL; - s->set_mute = NULL; - s->get_mute = NULL; - s->set_state = NULL; - s->update_requested_latency = NULL; + reset_callbacks(s); s->userdata = NULL; s->asyncmsgq = NULL; s->rtpoll = NULL; + pa_silence_memchunk_get( + &core->silence_cache, + core->mempool, + &s->silence, + &s->sample_spec, + 0); + + s->min_latency = DEFAULT_MIN_LATENCY; + s->max_latency = s->min_latency; + s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); s->thread_info.soft_volume = s->volume; s->thread_info.soft_muted = s->muted; s->thread_info.state = s->state; + s->thread_info.max_rewind = 0; s->thread_info.requested_latency_valid = TRUE; s->thread_info.requested_latency = 0; @@ -247,8 +261,8 @@ void pa_source_put(pa_source *s) { pa_source_assert_ref(s); pa_assert(s->state == PA_SINK_INIT); - pa_assert(s->rtpoll); pa_assert(s->asyncmsgq); + pa_assert(s->rtpoll); pa_assert(!s->min_latency || !s->max_latency || s->min_latency <= s->max_latency); @@ -290,13 +304,7 @@ void pa_source_unlink(pa_source *s) { else s->state = PA_SOURCE_UNLINKED; - s->get_latency = NULL; - s->get_volume = NULL; - s->set_volume = NULL; - s->set_mute = NULL; - s->get_mute = NULL; - s->set_state = NULL; - s->update_requested_latency = NULL; + reset_callbacks(s); if (linked) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_REMOVE, s->index); @@ -323,6 +331,9 @@ static void source_free(pa_object *o) { pa_hashmap_free(s->thread_info.outputs, NULL, NULL); + if (s->silence.memblock) + pa_memblock_unref(s->silence.memblock); + pa_xfree(s->name); pa_xfree(s->driver); @@ -332,6 +343,20 @@ static void source_free(pa_object *o) { pa_xfree(s); } +void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) { + pa_source_assert_ref(s); + pa_assert(q); + + s->asyncmsgq = q; +} + +void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) { + pa_source_assert_ref(s); + pa_assert(p); + + s->rtpoll = p; +} + int pa_source_update_status(pa_source*s) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_LINKED(s->state)); @@ -352,11 +377,22 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend) { return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE); } -void pa_source_ping(pa_source *s) { +void pa_source_process_rewind(pa_source *s, size_t nbytes) { + pa_source_output *o; + void *state = NULL; + pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_OPENED(s->thread_info.state)); + + if (nbytes <= 0) + return; - pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_PING, NULL, 0, NULL, NULL); + pa_log_debug("Processing rewind..."); + + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) { + pa_source_output_assert_ref(o); + pa_source_output_process_rewind(o, nbytes); + } } void pa_source_post(pa_source*s, const pa_memchunk *chunk) { @@ -381,14 +417,18 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { else pa_volume_memchunk(&vchunk, &s->sample_spec, &s->thread_info.soft_volume); - while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) { + pa_source_output_assert_ref(o); pa_source_output_push(o, &vchunk); + } pa_memblock_unref(vchunk.memblock); } else { - while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) { + pa_source_output_assert_ref(o); pa_source_output_push(o, chunk); + } } } @@ -497,6 +537,7 @@ void pa_source_set_description(pa_source *s, const char *description) { return; old = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION); + if (old && description && !strcmp(old, description)) return; @@ -511,20 +552,6 @@ void pa_source_set_description(pa_source *s, const char *description) { } } -void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) { - pa_source_assert_ref(s); - pa_assert(q); - - s->asyncmsgq = q; -} - -void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) { - pa_source_assert_ref(s); - pa_assert(p); - - s->rtpoll = p; -} - unsigned pa_source_linked_by(pa_source *s) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_LINKED(s->state)); @@ -555,6 +582,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o)); + pa_source_output_set_max_rewind(o, s->thread_info.max_rewind); + pa_assert(!o->thread_info.attached); o->thread_info.attached = TRUE; @@ -599,9 +628,6 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ *((pa_bool_t*) userdata) = s->thread_info.soft_muted; return 0; - case PA_SOURCE_MESSAGE_PING: - return 0; - case PA_SOURCE_MESSAGE_SET_STATE: s->thread_info.state = PA_PTR_TO_UINT(userdata); return 0; @@ -731,6 +757,21 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) { return usec; } +void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) { + pa_source_output *o; + void *state = NULL; + + pa_source_assert_ref(s); + + if (max_rewind == s->thread_info.max_rewind) + return; + + s->thread_info.max_rewind = max_rewind; + + while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) + pa_source_output_set_max_rewind(o, s->thread_info.max_rewind); +} + void pa_source_invalidate_requested_latency(pa_source *s) { pa_source_assert_ref(s); diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index ab7236eb..b8859c84 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -91,6 +91,8 @@ struct pa_source { pa_asyncmsgq *asyncmsgq; pa_rtpoll *rtpoll; + pa_memchunk silence; + pa_usec_t min_latency; /* we won't go below this latency setting */ pa_usec_t max_latency; /* An upper limit for the latencies */ @@ -112,6 +114,10 @@ struct pa_source { pa_bool_t requested_latency_valid; size_t requested_latency; + + /* Then number of bytes this source will be rewound for at + * max */ + size_t max_rewind; } thread_info; void *userdata; @@ -130,7 +136,6 @@ typedef enum pa_source_message { PA_SOURCE_MESSAGE_GET_LATENCY, PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, PA_SOURCE_MESSAGE_SET_STATE, - PA_SOURCE_MESSAGE_PING, PA_SOURCE_MESSAGE_ATTACH, PA_SOURCE_MESSAGE_DETACH, PA_SOURCE_MESSAGE_MAX @@ -189,8 +194,6 @@ int pa_source_update_status(pa_source*s); int pa_source_suspend(pa_source *s, pa_bool_t suspend); int pa_source_suspend_all(pa_core *c, pa_bool_t suspend); -void pa_source_ping(pa_source *s); - void pa_source_set_volume(pa_source *source, const pa_cvolume *volume); const pa_cvolume *pa_source_get_volume(pa_source *source); void pa_source_set_mute(pa_source *source, pa_bool_t mute); @@ -203,6 +206,7 @@ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that ar /* To be called exclusively by the source driver, from IO context */ void pa_source_post(pa_source*s, const pa_memchunk *b); +void pa_source_process_rewind(pa_source *s, size_t nbytes); int pa_source_process_msg(pa_msgobject *o, int code, void *userdata, int64_t, pa_memchunk *chunk); @@ -211,6 +215,8 @@ void pa_source_detach_within_thread(pa_source *s); pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s); +void pa_source_set_max_rewind(pa_source *s, size_t max_rewind); + /* To be called exclusively by source output drivers, from IO context */ void pa_source_invalidate_requested_latency(pa_source *s); diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c index 402c8579..3fa8d79f 100644 --- a/src/tests/memblockq-test.c +++ b/src/tests/memblockq-test.c @@ -61,16 +61,18 @@ int main(int argc, char *argv[]) { pa_mempool *p; pa_memblockq *bq; pa_memchunk chunk1, chunk2, chunk3, chunk4; - pa_memblock *silence; + pa_memchunk silence; pa_log_set_maximal_level(PA_LOG_DEBUG); p = pa_mempool_new(0); - silence = pa_memblock_new_fixed(p, (char*) "__", 2, 1); - assert(silence); + silence.memblock = pa_memblock_new_fixed(p, (char*) "__", 2, 1); + assert(silence.memblock); + silence.index = 0; + silence.length = pa_memblock_get_length(silence.memblock); - bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, 40, silence); + bq = pa_memblockq_new(0, 40, 10, 2, 4, 4, 40, &silence); assert(bq); chunk1.memblock = pa_memblock_new_fixed(p, (char*) "11", 2, 1); @@ -152,7 +154,7 @@ int main(int argc, char *argv[]) { dump(bq); pa_memblockq_free(bq); - pa_memblock_unref(silence); + pa_memblock_unref(silence.memblock); pa_memblock_unref(chunk1.memblock); pa_memblock_unref(chunk2.memblock); pa_memblock_unref(chunk3.memblock); -- cgit From 5971345e9951762ec27af3d824b2909c034b1c48 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 20:35:44 +0000 Subject: rename sink_input->rewind to process_rewind() and set_max_rewind to update_max_rewind() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2284 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/play-memblockq.c | 8 ++++---- src/pulsecore/protocol-native.c | 12 ++++++------ src/pulsecore/sink-input.c | 16 ++++++++-------- src/pulsecore/sink-input.h | 6 +++--- src/pulsecore/sink.c | 6 +++--- src/pulsecore/sound-file-stream.c | 8 ++++---- src/pulsecore/source-output.c | 16 ++++++++-------- src/pulsecore/source-output.h | 6 +++--- src/pulsecore/source.c | 4 ++-- 9 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 7b9b8fb8..99b59134 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -127,7 +127,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk return 0; } -static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { memblockq_stream *u; pa_sink_input_assert_ref(i); @@ -141,7 +141,7 @@ static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(u->memblockq, nbytes); } -static void sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes) { +static void sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes) { memblockq_stream *u; pa_sink_input_assert_ref(i); @@ -193,8 +193,8 @@ pa_sink_input* pa_memblockq_sink_input_new( goto fail; u->sink_input->pop = sink_input_pop_cb; - u->sink_input->rewind = sink_input_rewind_cb; - u->sink_input->set_max_rewind = sink_input_set_max_rewind; + u->sink_input->process_rewind = sink_input_process_rewind_cb; + u->sink_input->update_max_rewind = sink_input_update_max_rewind; u->sink_input->kill = sink_input_kill_cb; u->sink_input->userdata = u; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 37200db6..42baf8d8 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -209,8 +209,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk static void sink_input_kill_cb(pa_sink_input *i); static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend); static void sink_input_moved_cb(pa_sink_input *i); -static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes); -static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes); +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes); +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes); static void send_memblock(connection *c); @@ -760,8 +760,8 @@ static playback_stream* playback_stream_new( s->sink_input->parent.process_msg = sink_input_process_msg; s->sink_input->pop = sink_input_pop_cb; - s->sink_input->rewind = sink_input_rewind_cb; - s->sink_input->set_max_rewind = sink_input_set_max_rewind_cb; + s->sink_input->process_rewind = sink_input_process_rewind_cb; + s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; s->sink_input->kill = sink_input_kill_cb; s->sink_input->moved = sink_input_moved_cb; s->sink_input->suspend = sink_input_suspend_cb; @@ -1228,7 +1228,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk return 0; } -static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { playback_stream *s; pa_sink_input_assert_ref(i); @@ -1242,7 +1242,7 @@ static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(s->memblockq, nbytes); } -static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { playback_stream *s; pa_sink_input_assert_ref(i); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 7e6c5de1..ccb1ff11 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -98,8 +98,8 @@ static void reset_callbacks(pa_sink_input *i) { pa_assert(i); i->pop = NULL; - i->rewind = NULL; - i->set_max_rewind = NULL; + i->process_rewind = NULL; + i->update_max_rewind = NULL; i->attach = NULL; i->detach = NULL; i->suspend = NULL; @@ -394,7 +394,7 @@ void pa_sink_input_put(pa_sink_input *i) { pa_assert(i->state == PA_SINK_INPUT_INIT); pa_assert(i->pop); - pa_assert(i->rewind); + pa_assert(i->process_rewind); i->thread_info.volume = i->volume; i->thread_info.muted = i->muted; @@ -651,8 +651,8 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); /* Tell the implementor */ - if (i->rewind) - i->rewind(i, amount); + if (i->process_rewind) + i->process_rewind(i, amount); } /* And reset the resampler */ @@ -668,15 +668,15 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam } /* Called from thread context */ -void pa_sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) { +void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes); - if (i->set_max_rewind) - i->set_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes); + if (i->update_max_rewind) + i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes); } pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 184a2c4e..b433edc0 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -103,11 +103,11 @@ struct pa_sink_input { * before peek() if it is called at all. Only called if the sink * input driver ever plans to call * pa_sink_input_request_rewind(). Called from IO context. */ - void (*rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */ + void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */ /* Called whenever the maximum rewindable size of the sink * changes. Called from RT context. */ - void (*set_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ + void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ /* If non-NULL this function is called when the input is first * connected to a sink or when the rtpoll/asyncmsgq fields @@ -267,7 +267,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i); int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume); void pa_sink_input_drop(pa_sink_input *i, size_t length); void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); -void pa_sink_input_set_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); +void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index f631de35..39468f1e 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -981,7 +981,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse i->thread_info.sync_next->thread_info.sync_prev = i; } - pa_sink_input_set_max_rewind(i, s->thread_info.max_rewind); + pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); pa_assert(!i->thread_info.attached); i->thread_info.attached = TRUE; @@ -1111,7 +1111,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(info->ghost_sink_input->index), pa_sink_input_ref(info->ghost_sink_input)); info->ghost_sink_input->thread_info.sync_prev = info->ghost_sink_input->thread_info.sync_next = NULL; - pa_sink_input_set_max_rewind(info->ghost_sink_input, s->thread_info.max_rewind); + pa_sink_input_update_max_rewind(info->ghost_sink_input, s->thread_info.max_rewind); pa_assert(!info->ghost_sink_input->thread_info.attached); info->ghost_sink_input->thread_info.attached = TRUE; @@ -1314,7 +1314,7 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { s->thread_info.max_rewind = max_rewind; while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) - pa_sink_input_set_max_rewind(i, s->thread_info.max_rewind); + pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); if (s->monitor_source) pa_source_set_max_rewind(s->monitor_source, s->thread_info.max_rewind); diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 9030d6db..604723f1 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -198,7 +198,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk return -1; } -static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { file_stream *u; pa_sink_input_assert_ref(i); @@ -212,7 +212,7 @@ static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(u->memblockq, nbytes); } -static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { file_stream *u; pa_sink_input_assert_ref(i); @@ -331,8 +331,8 @@ int pa_play_file( goto fail; u->sink_input->pop = sink_input_pop_cb; - u->sink_input->rewind = sink_input_rewind_cb; - u->sink_input->set_max_rewind = sink_input_set_max_rewind_cb; + u->sink_input->process_rewind = sink_input_process_rewind_cb; + u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->userdata = u; diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 318f6f84..c6dc9e88 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -80,8 +80,8 @@ static void reset_callbacks(pa_source_output *o) { pa_assert(o); o->push = NULL; - o->rewind = NULL; - o->set_max_rewind = NULL; + o->process_rewind = NULL; + o->update_max_rewind = NULL; o->attach = NULL; o->detach = NULL; o->suspend = NULL; @@ -376,7 +376,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_memblockq_seek(o->thread_info.delay_memblockq, chunk->length, PA_SEEK_RELATIVE); } - limit = o->rewind ? 0 : o->source->thread_info.max_rewind; + limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind; /* Implement the delay queue */ while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) { @@ -418,7 +418,7 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in si if (nbytes <= 0) return; - if (o->rewind) { + if (o->process_rewind) { pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0); if (o->thread_info.resampler) @@ -427,7 +427,7 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in si pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes); if (nbytes > 0) - o->rewind(o, nbytes); + o->process_rewind(o, nbytes); if (o->thread_info.resampler) pa_resampler_reset(o->thread_info.resampler); @@ -437,13 +437,13 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in si } /* Called from thread context */ -void pa_source_output_set_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) { +void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); - if (o->set_max_rewind) - o->set_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes); + if (o->update_max_rewind) + o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes); } pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index f79761ae..e7d8963f 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -84,11 +84,11 @@ struct pa_source_output { /* Only relevant for monitor sources right now: called when the * recorded stream is rewound. */ - void (*rewind)(pa_source_output *o, size_t nbytes); + void (*process_rewind)(pa_source_output *o, size_t nbytes); /* Called whenever the maximum rewindable size of the source * changes. Called from RT context. */ - void (*set_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */ + void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */ /* If non-NULL this function is called when the output is first * connected to a source. Called from IO thread context */ @@ -209,7 +209,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest); void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes); -void pa_source_output_set_max_rewind(pa_source_output *o, size_t nbytes); +void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes); int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 0de78f41..8138a43f 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -582,7 +582,7 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o)); - pa_source_output_set_max_rewind(o, s->thread_info.max_rewind); + pa_source_output_update_max_rewind(o, s->thread_info.max_rewind); pa_assert(!o->thread_info.attached); o->thread_info.attached = TRUE; @@ -769,7 +769,7 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) { s->thread_info.max_rewind = max_rewind; while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) - pa_source_output_set_max_rewind(o, s->thread_info.max_rewind); + pa_source_output_update_max_rewind(o, s->thread_info.max_rewind); } void pa_source_invalidate_requested_latency(pa_source *s) { -- cgit From 64e048cb1b170636e72b6a15afa3b6b8b90bb366 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 20:53:46 +0000 Subject: drop a redundant pa_init_proplist(), properly set MEDIA_NAME property on stream, not on context git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2285 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/stream.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pulse/stream.c b/src/pulse/stream.c index f047acfe..e67f24c0 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -95,7 +95,7 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa s->proplist = p ? pa_proplist_copy(p) : pa_proplist_new(); if (name) - pa_proplist_sets(c->proplist, PA_PROP_MEDIA_NAME, name); + pa_proplist_sets(s->proplist, PA_PROP_MEDIA_NAME, name); s->channel = 0; s->channel_valid = 0; @@ -843,8 +843,6 @@ static int create_stream( else pa_tagstruct_put_boolean(t, flags & PA_STREAM_PEAK_DETECT); - pa_init_proplist(s->proplist); - pa_tagstruct_put( t, PA_TAG_BOOLEAN, flags & PA_STREAM_ADJUST_LATENCY, -- cgit From c2c833c068a38ab2d5bea1cd9515cda8ad93b28d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 21:49:05 +0000 Subject: use the sink description instead of the name to choose the description for the monitor source git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2286 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 39468f1e..34fb7ae9 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -124,10 +124,10 @@ pa_sink* pa_sink_new( pa_sink_flags_t flags) { pa_sink *s; - char *d; const char *name; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_source_new_data source_data; + const char *dn; pa_assert(core); pa_assert(data); @@ -235,9 +235,8 @@ pa_sink* pa_sink_new( source_data.driver = data->driver; source_data.module = data->module; - d = pa_sprintf_malloc("Monitor Source of %s", s->name); - pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, d); - pa_xfree(d); + dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION); + pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor Source of %s", dn ? dn : s->name); pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor"); s->monitor_source = pa_source_new(core, &source_data, 0); -- cgit From 8181db182b1612df48dec07b6904fa9ce32b6bd8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 21:50:57 +0000 Subject: initialize properties for ALSA sinks/sources more elaborately, re #277 git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2287 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.c | 73 ++++++++++++++++++++++++++++++++++++++++ src/modules/alsa-util.h | 3 +- src/modules/module-alsa-sink.c | 15 ++------- src/modules/module-alsa-source.c | 13 +------ 4 files changed, 78 insertions(+), 26 deletions(-) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 9c9fc72f..d0565556 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -973,3 +973,76 @@ void pa_alsa_redirect_errors_dec(void) { if (r == 1) snd_lib_error_set_handler(NULL); } + + +void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { + + static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = { + [SND_PCM_CLASS_GENERIC] = "generic", + [SND_PCM_CLASS_MULTI] = "multi", + [SND_PCM_CLASS_MODEM] = "modem", + [SND_PCM_CLASS_DIGITIZER] = "digitizer" + }; + static const char * const class_table[SND_PCM_CLASS_LAST+1] = { + [SND_PCM_CLASS_GENERIC] = "sound", + [SND_PCM_CLASS_MULTI] = NULL, + [SND_PCM_CLASS_MODEM] = "modem", + [SND_PCM_CLASS_DIGITIZER] = NULL + }; + static const char * const alsa_subclass_table[SND_PCM_SUBCLASS_LAST+1] = { + [SND_PCM_SUBCLASS_GENERIC_MIX] = "generic-mix", + [SND_PCM_SUBCLASS_MULTI_MIX] = "multi-mix" + }; + + snd_pcm_class_t class; + snd_pcm_subclass_t subclass; + const char *n, *id, *sdn; + char *cn = NULL, *lcn = NULL; + int card; + + pa_assert(p); + pa_assert(pcm_info); + + pa_proplist_sets(p, PA_PROP_DEVICE_API, "alsa"); + + class = snd_pcm_info_get_class(pcm_info); + if (class <= SND_PCM_CLASS_LAST) { + if (class_table[class]) + pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, class_table[class]); + if (alsa_class_table[class]) + pa_proplist_sets(p, "alsa.class", alsa_class_table[class]); + } + subclass = snd_pcm_info_get_subclass(pcm_info); + if (subclass <= SND_PCM_SUBCLASS_LAST) + if (alsa_subclass_table[subclass]) + pa_proplist_sets(p, "alsa.subclass", alsa_subclass_table[subclass]); + + if ((n = snd_pcm_info_get_name(pcm_info))) + pa_proplist_sets(p, "alsa.name", n); + + if ((id = snd_pcm_info_get_id(pcm_info))) + pa_proplist_sets(p, "alsa.id", id); + + pa_proplist_setf(p, "alsa.subdevice", "%u", snd_pcm_info_get_subdevice(pcm_info)); + if ((sdn = snd_pcm_info_get_subdevice_name(pcm_info))) + pa_proplist_sets(p, "alsa.subdevice_name", sdn); + + pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info)); + + if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) { + pa_proplist_setf(p, "card", "%i", card); + + if (snd_card_get_name(card, &cn) >= 0) + pa_proplist_sets(p, "alsa.card_name", cn); + + if (snd_card_get_longname(card, &lcn) >= 0) + pa_proplist_sets(p, "alsa.long_card_name", lcn); + } + + if (cn && n) + pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s - %s", cn, n); + else if (cn) + pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn); + else if (n) + pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n); +} diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 51222a4f..03de61e2 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -29,8 +29,8 @@ #include #include - #include +#include typedef struct pa_alsa_fdlist pa_alsa_fdlist; @@ -88,5 +88,6 @@ void pa_alsa_dump_status(snd_pcm_t *pcm); void pa_alsa_redirect_errors_inc(void); void pa_alsa_redirect_errors_dec(void); +void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info); #endif diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 32bfc304..2c993d6f 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -93,7 +93,7 @@ static const char* const valid_modargs[] = { #define DEFAULT_DEVICE "default" #define DEFAULT_TSCHED_BUFFER_USEC (10*PA_USEC_PER_SEC) /* 10s */ -#define DEFAULT_TSCHED_WATERMARK_USEC (200*PA_USEC_PER_MSEC) /* 20ms */ +#define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC) /* 20ms */ struct userdata { pa_core *core; @@ -1050,12 +1050,6 @@ int pa__init(pa_module*m) { pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE; pa_usec_t usec; pa_sink_new_data data; - static const char * const class_table[SND_PCM_CLASS_LAST+1] = { - [SND_PCM_CLASS_GENERIC] = "sound", - [SND_PCM_CLASS_MULTI] = NULL, - [SND_PCM_CLASS_MODEM] = "modem", - [SND_PCM_CLASS_DIGITIZER] = NULL - }; snd_pcm_info_alloca(&pcm_info); @@ -1242,15 +1236,10 @@ int pa__init(pa_module*m) { pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); + pa_alsa_init_proplist(data.proplist, pcm_info); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "alsa"); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, snd_pcm_info_get_name(pcm_info)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); - - if (class_table[snd_pcm_info_get_class(pcm_info)]) - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, class_table[snd_pcm_info_get_class(pcm_info)]); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 185acc01..e6ea2d75 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -880,12 +880,6 @@ int pa__init(pa_module*m) { pa_bool_t namereg_fail; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, mixer_reset = TRUE; pa_source_new_data data; - static const char * const class_table[SND_PCM_CLASS_LAST+1] = { - [SND_PCM_CLASS_GENERIC] = "sound", - [SND_PCM_CLASS_MULTI] = NULL, - [SND_PCM_CLASS_MODEM] = "modem", - [SND_PCM_CLASS_DIGITIZER] = NULL - }; snd_pcm_info_alloca(&pcm_info); @@ -1067,15 +1061,10 @@ int pa__init(pa_module*m) { pa_source_new_data_set_sample_spec(&data, &ss); pa_source_new_data_set_channel_map(&data, &map); + pa_alsa_init_proplist(data.proplist, pcm_info); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "alsa"); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, snd_pcm_info_get_name(pcm_info)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); - - if (class_table[snd_pcm_info_get_class(pcm_info)]) - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, class_table[snd_pcm_info_get_class(pcm_info)]); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap_rewrite" : (u->use_mmap ? "mmap" : "serial")); u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); -- cgit From ba6c0e17cf91b8a93179e9e9f05c492836015f4d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 23:56:55 +0000 Subject: fix C++ compat git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2288 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/proplist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 302a59db..40c47759 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -192,7 +192,7 @@ 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.11 */ -pa_proplist* pa_proplist_copy(pa_proplist *template); +pa_proplist* pa_proplist_copy(pa_proplist *t); PA_C_DECL_END -- cgit From 5e7e827be5988db0262830886c7c9213fe45b554 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 20 Apr 2008 23:57:44 +0000 Subject: improve dB volume calculation git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2289 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/volume.c | 4 ++-- src/pulse/volume.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 3688b847..33ab1c5f 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -80,10 +80,10 @@ pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a)* pa_sw_volume_to_linear(b)); } -#define USER_DECIBEL_RANGE 30 +#define USER_DECIBEL_RANGE 60 pa_volume_t pa_sw_volume_from_dB(double dB) { - if (dB <= -USER_DECIBEL_RANGE) + if (isinf(dB) < 0 || dB <= -USER_DECIBEL_RANGE) return PA_VOLUME_MUTED; return (pa_volume_t) ((dB/USER_DECIBEL_RANGE+1)*PA_VOLUME_NORM); diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 245cc129..e7ceb0d7 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -166,10 +166,10 @@ pa_volume_t pa_sw_volume_from_linear(double v) PA_GCC_CONST; double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST; #ifdef INFINITY -#define PA_DECIBEL_MININFTY (-INFINITY) +#define PA_DECIBEL_MININFTY ((double) -INFINITY) #else /** This value is used as minus infinity when using pa_volume_{to,from}_dB(). */ -#define PA_DECIBEL_MININFTY (-200) +#define PA_DECIBEL_MININFTY ((double) -200) #endif PA_C_DECL_END -- cgit From 88227c41c1dc9505b043b6a37ca86eaafe5b549f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 00:52:31 +0000 Subject: properly initialize memblock->is_silence for imported memory blocks; make is_silence and read_only a bit field git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2290 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/memblock.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index c452ae0b..46537795 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -59,7 +59,7 @@ struct pa_memblock { pa_mempool *pool; pa_memblock_type_t type; - pa_bool_t read_only, is_silence; + pa_bool_t read_only:1, is_silence:1; pa_atomic_ptr_t data; size_t length; @@ -226,8 +226,7 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) { PA_REFCNT_INIT(b); b->pool = p; b->type = PA_MEMBLOCK_APPENDED; - b->read_only = FALSE; - b->is_silence = FALSE; + b->read_only = b->is_silence = FALSE; pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); b->length = length; pa_atomic_store(&b->n_acquired, 0); @@ -331,8 +330,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { PA_REFCNT_INIT(b); b->pool = p; - b->read_only = FALSE; - b->is_silence = FALSE; + b->read_only = b->is_silence = FALSE; b->length = length; pa_atomic_store(&b->n_acquired, 0); pa_atomic_store(&b->please_signal, 0); @@ -598,7 +596,7 @@ static void memblock_make_local(pa_memblock *b) { pa_atomic_ptr_store(&b->data, new_data); b->type = PA_MEMBLOCK_POOL_EXTERNAL; - b->read_only = 0; + b->read_only = FALSE; goto finish; } @@ -609,7 +607,7 @@ static void memblock_make_local(pa_memblock *b) { pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length)); b->type = PA_MEMBLOCK_USER; - b->read_only = 0; + b->read_only = FALSE; finish: pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]); @@ -905,7 +903,8 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i PA_REFCNT_INIT(b); b->pool = i->pool; b->type = PA_MEMBLOCK_IMPORTED; - b->read_only = 1; + b->read_only = TRUE; + b->is_silence = FALSE; pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset); b->length = size; pa_atomic_store(&b->n_acquired, 0); -- cgit From e16a1987a3ffa33b4a04a4c3a02320d79fd14195 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 01:04:27 +0000 Subject: - Change meaning of special values of latency request: 0 -> "minimal latency, please"; (pa_usec_t)-1 -> "don't care" - Remove "source" word from monitor source description - Increase default tsched watermark to 20ms again - For the first iteration after snd_pcm_start() halve the sleep time as workaround for USB devices with quick starts git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2291 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 22 ++++++++++++++-------- src/modules/module-alsa-source.c | 5 ++++- src/pulsecore/sink-input.c | 6 ++---- src/pulsecore/sink.c | 12 ++++++------ src/pulsecore/source-output.c | 4 ++-- src/pulsecore/source.c | 10 +++++----- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 2c993d6f..c3ae6e65 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -93,7 +93,7 @@ static const char* const valid_modargs[] = { #define DEFAULT_DEVICE "default" #define DEFAULT_TSCHED_BUFFER_USEC (10*PA_USEC_PER_SEC) /* 10s */ -#define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC) /* 20ms */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ struct userdata { pa_core *core; @@ -476,7 +476,7 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { usec = pa_sink_get_requested_latency_within_thread(u->sink); - if (usec <= 0) + if (usec == (pa_usec_t) -1) usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec); /* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ @@ -488,6 +488,11 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { else usec /= 2; + if (u->first) { + pa_log_debug("Decreasing wakeup time for the first iteration by half."); + usec /= 2; + } + /* pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ return usec; @@ -503,10 +508,10 @@ static int update_sw_params(struct userdata *u) { u->hwbuf_unused_frames = 0; if (u->use_tsched) - if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) > 0) { + if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) != (pa_usec_t) -1) { size_t b; - pa_log("latency set to %llu", (unsigned long long) latency); + pa_log_debug("latency set to %llu", (unsigned long long) latency); b = pa_usec_to_bytes(latency, &u->sink->sample_spec); @@ -520,7 +525,7 @@ static int update_sw_params(struct userdata *u) { ((u->hwbuf_size - b) / u->frame_size) : 0; } - pa_log("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames); + pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames); /* We need at last one frame in the used part of the buffer */ u->avail_min_frames = u->hwbuf_unused_frames + 1; @@ -533,7 +538,7 @@ static int update_sw_params(struct userdata *u) { u->avail_min_frames += (pa_usec_to_bytes(usec, &u->sink->sample_spec) / u->frame_size); } - pa_log("setting avail_min=%lu", (unsigned long) u->avail_min_frames); + pa_log_debug("setting avail_min=%lu", (unsigned long) u->avail_min_frames); if ((err = pa_alsa_set_sw_params(u->pcm_handle, u->avail_min_frames)) < 0) { pa_log("Failed to set software parameters: %s", snd_strerror(err)); @@ -918,7 +923,6 @@ static void thread_func(void *userdata) { if (u->first) { pa_log_info("Starting playback."); snd_pcm_start(u->pcm_handle); - u->first = FALSE; pa_smoother_resume(u->smoother, pa_rtclock_usec()); } @@ -946,6 +950,8 @@ static void thread_func(void *userdata) { pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); } + u->first = FALSE; + } else if (u->use_tsched) /* OK, we're in an invalid state, let's disable our timers */ @@ -1018,7 +1024,7 @@ static void thread_func(void *userdata) { } if (revents) - pa_log_info("Wakeup from ALSA! (%i)", revents); + pa_log_debug("Wakeup from ALSA! (%i)", revents); } } diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index e6ea2d75..a0677f8d 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -415,7 +415,7 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { usec = pa_source_get_requested_latency_within_thread(u->source); - if (usec <= 0) + if (usec == (pa_usec_t) -1) usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); @@ -850,6 +850,9 @@ static void thread_func(void *userdata) { snd_pcm_start(u->pcm_handle); } + + if (revents) + pa_log_debug("Wakeup from ALSA! (%i)", revents); } } diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index ccb1ff11..8df36876 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -247,7 +247,7 @@ pa_sink_input* pa_sink_input_new( i->thread_info.resampler = resampler; i->thread_info.volume = i->volume; i->thread_info.muted = i->muted; - i->thread_info.requested_sink_latency = 0; + i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.rewrite_nbytes = 0; i->thread_info.since_underrun = 0; i->thread_info.ignore_rewind = FALSE; @@ -534,8 +534,6 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa if (do_volume_adj_here && !volume_is_norm) { pa_memchunk_make_writable(&wchunk, 0); - pa_log_debug("adjusting volume!"); - if (i->thread_info.muted) pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec); else @@ -682,7 +680,7 @@ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { pa_sink_input_assert_ref(i); - if (usec > 0) { + if (usec != (pa_usec_t) -1) { if (i->sink->max_latency > 0 && usec > i->sink->max_latency) usec = i->sink->max_latency; diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 34fb7ae9..e88e7d2b 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -217,7 +217,7 @@ pa_sink* pa_sink_new( s->thread_info.state = s->state; s->thread_info.rewind_nbytes = 0; s->thread_info.max_rewind = 0; - s->thread_info.requested_latency_valid = TRUE; + s->thread_info.requested_latency_valid = FALSE; s->thread_info.requested_latency = 0; pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0); @@ -236,7 +236,7 @@ pa_sink* pa_sink_new( source_data.module = data->module; dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor Source of %s", dn ? dn : s->name); + pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name); pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "monitor"); s->monitor_source = pa_source_new(core, &source_data, 0); @@ -1257,7 +1257,7 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes) { } pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) { - pa_usec_t result = 0; + pa_usec_t result = (pa_usec_t) -1; pa_sink_input *i; void *state = NULL; @@ -1268,11 +1268,11 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) { while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) - if (i->thread_info.requested_sink_latency > 0 && - (!result || result > i->thread_info.requested_sink_latency)) + if (i->thread_info.requested_sink_latency != (pa_usec_t) -1 && + (result == (pa_usec_t) -1 || result > i->thread_info.requested_sink_latency)) result = i->thread_info.requested_sink_latency; - if (result > 0) { + if (result != (pa_usec_t) -1) { if (s->max_latency > 0 && result > s->max_latency) result = s->max_latency; diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index c6dc9e88..9c75b39c 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -198,7 +198,7 @@ pa_source_output* pa_source_output_new( o->thread_info.attached = FALSE; o->thread_info.sample_spec = o->sample_spec; o->thread_info.resampler = resampler; - o->thread_info.requested_source_latency = 0; + o->thread_info.requested_source_latency = (pa_usec_t) -1; o->thread_info.delay_memblockq = pa_memblockq_new( 0, @@ -449,7 +449,7 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); - if (usec > 0) { + if (usec != (pa_usec_t) -1) { if (o->source->max_latency > 0 && usec > o->source->max_latency) usec = o->source->max_latency; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 8138a43f..27279719 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -204,7 +204,7 @@ pa_source* pa_source_new( s->thread_info.soft_muted = s->muted; s->thread_info.state = s->state; s->thread_info.max_rewind = 0; - s->thread_info.requested_latency_valid = TRUE; + s->thread_info.requested_latency_valid = FALSE; s->thread_info.requested_latency = 0; pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0); @@ -713,7 +713,7 @@ void pa_source_attach_within_thread(pa_source *s) { } pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) { - pa_usec_t result = 0; + pa_usec_t result = (pa_usec_t) -1; pa_source_output *o; void *state = NULL; @@ -724,11 +724,11 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) { while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) - if (o->thread_info.requested_source_latency > 0 && - (!result || result > o->thread_info.requested_source_latency)) + if (o->thread_info.requested_source_latency != (pa_usec_t) -1 && + (result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency)) result = o->thread_info.requested_source_latency; - if (result > 0) { + if (result != (pa_usec_t) -1) { if (s->max_latency > 0 && result > s->max_latency) result = s->max_latency; -- cgit From 0d01c4350b9b0a504e47141e89383d03c59c5e7f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 01:11:16 +0000 Subject: make sure the client buffer has space for 2*minreq+tlength. Explain why git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2292 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-native.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 42baf8d8..7e86f117 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -793,9 +793,21 @@ static playback_stream* playback_stream_new( * half the latency will be spent on the hw buffer, the other * half of it in the async buffer queue we maintain for each * client. In between we'll have a safety space of size - * minreq.*/ - - sink_usec = (tlength_usec-minreq_usec)/2; + * 2*minreq. Why the 2*minreq? When the hw buffer is completey + * empty and needs to be filled, then our buffer must have + * enough data to fulfill this request immediatly and thus + * have at least the same tlength as the size of the hw + * buffer. It additionally needs space for 2 times minreq + * because if the buffer ran empty and a partial fillup + * happens immediately on the next iteration we need to be + * able to fulfill it and give the application also minreq + * time to fill it up again for the next request Makes 2 times + * minreq in plus.. */ + + if (tlength_usec > minreq_usec*2) + sink_usec = (tlength_usec - minreq_usec*2)/2; + else + sink_usec = 0; } else { @@ -803,7 +815,10 @@ static playback_stream* playback_stream_new( * still need to make sure that the parameters from the user * do make sense. */ - sink_usec = tlength_usec - minreq_usec; + if (tlength_usec > minreq_usec*2) + sink_usec = (tlength_usec - minreq_usec*2); + else + sink_usec = 0; } s->sink_latency = pa_sink_input_set_requested_latency(sink_input, sink_usec); @@ -817,19 +832,19 @@ static playback_stream* playback_stream_new( tlength_usec -= s->sink_latency; } - if (tlength_usec < s->sink_latency + minreq_usec) - tlength_usec = s->sink_latency + minreq_usec; + if (tlength_usec < s->sink_latency + 2*minreq_usec) + tlength_usec = s->sink_latency + 2*minreq_usec; *tlength = pa_usec_to_bytes(tlength_usec, &sink_input->sample_spec); *minreq = pa_usec_to_bytes(minreq_usec, &sink_input->sample_spec); if (*minreq <= 0) { - *minreq = frame_size; - *tlength += frame_size; + *minreq += frame_size; + *tlength += frame_size*2; } if (*tlength <= *minreq) - *tlength = *minreq + frame_size; + *tlength = *minreq*2 + frame_size; if (*prebuf <= 0) *prebuf = *tlength; -- cgit From 9a486efa5f85b83a1b9d006c8b4eac04092501ed Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 02:38:51 +0000 Subject: implement --process-time git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2293 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/utils/pacat.c | 59 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/utils/pacat.c b/src/utils/pacat.c index c05bff76..e802015b 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -71,7 +71,7 @@ static int channel_map_set = 0; static pa_stream_flags_t flags = 0; -static size_t latency = 0; +static size_t latency = 0, process_time=0; /* A shortcut for terminating the application */ static void quit(int ret) { @@ -267,6 +267,7 @@ static void context_state_callback(pa_context *c, void *userdata) { if (latency > 0) { memset(&buffer_attr, 0, sizeof(buffer_attr)); buffer_attr.tlength = latency; + buffer_attr.minreq = process_time; flags |= PA_STREAM_ADJUST_LATENCY; } @@ -502,6 +503,7 @@ static void help(const char *argv0) { " --no-remix Don't upmix or downmix channels.\n" " --no-remap Map channels by index instead of name.\n" " --latency=BYTES Request the specified latency in bytes.\n" + " --process-time=BYTES Request the specified process time per request in bytes.\n" , argv0); } @@ -519,7 +521,8 @@ enum { ARG_FIX_CHANNELS, ARG_NO_REMAP, ARG_NO_REMIX, - ARG_LATENCY + ARG_LATENCY, + ARG_PROCESS_TIME }; int main(int argc, char *argv[]) { @@ -529,27 +532,28 @@ int main(int argc, char *argv[]) { pa_time_event *time_event = NULL; static const struct option long_options[] = { - {"record", 0, NULL, 'r'}, - {"playback", 0, NULL, 'p'}, - {"device", 1, NULL, 'd'}, - {"server", 1, NULL, 's'}, - {"client-name", 1, NULL, 'n'}, - {"stream-name", 1, NULL, ARG_STREAM_NAME}, - {"version", 0, NULL, ARG_VERSION}, - {"help", 0, NULL, 'h'}, - {"verbose", 0, NULL, 'v'}, - {"volume", 1, NULL, ARG_VOLUME}, - {"rate", 1, NULL, ARG_SAMPLERATE}, - {"format", 1, NULL, ARG_SAMPLEFORMAT}, - {"channels", 1, NULL, ARG_CHANNELS}, - {"channel-map", 1, NULL, ARG_CHANNELMAP}, - {"fix-format", 0, NULL, ARG_FIX_FORMAT}, - {"fix-rate", 0, NULL, ARG_FIX_RATE}, - {"fix-channels",0, NULL, ARG_FIX_CHANNELS}, - {"no-remap", 0, NULL, ARG_NO_REMAP}, - {"no-remix", 0, NULL, ARG_NO_REMIX}, - {"latency", 0, NULL, ARG_LATENCY}, - {NULL, 0, NULL, 0} + {"record", 0, NULL, 'r'}, + {"playback", 0, NULL, 'p'}, + {"device", 1, NULL, 'd'}, + {"server", 1, NULL, 's'}, + {"client-name", 1, NULL, 'n'}, + {"stream-name", 1, NULL, ARG_STREAM_NAME}, + {"version", 0, NULL, ARG_VERSION}, + {"help", 0, NULL, 'h'}, + {"verbose", 0, NULL, 'v'}, + {"volume", 1, NULL, ARG_VOLUME}, + {"rate", 1, NULL, ARG_SAMPLERATE}, + {"format", 1, NULL, ARG_SAMPLEFORMAT}, + {"channels", 1, NULL, ARG_CHANNELS}, + {"channel-map", 1, NULL, ARG_CHANNELMAP}, + {"fix-format", 0, NULL, ARG_FIX_FORMAT}, + {"fix-rate", 0, NULL, ARG_FIX_RATE}, + {"fix-channels", 0, NULL, ARG_FIX_CHANNELS}, + {"no-remap", 0, NULL, ARG_NO_REMAP}, + {"no-remix", 0, NULL, ARG_NO_REMIX}, + {"latency", 1, NULL, ARG_LATENCY}, + {"process-time", 1, NULL, ARG_PROCESS_TIME}, + {NULL, 0, NULL, 0} }; if (!(bn = strrchr(argv[0], '/'))) @@ -656,7 +660,14 @@ int main(int argc, char *argv[]) { case ARG_LATENCY: if (((latency = atoi(optarg))) <= 0) { - fprintf(stderr, "Invallid latency specification '%s'\n", optarg); + fprintf(stderr, "Invalid latency specification '%s'\n", optarg); + goto quit; + } + break; + + case ARG_PROCESS_TIME: + if (((process_time = atoi(optarg))) <= 0) { + fprintf(stderr, "Invalid process time specification '%s'\n", optarg); goto quit; } break; -- cgit From 69f6bdf1557fa9d8edd590222c17b5c2450ec8bd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 02:44:25 +0000 Subject: add new function pa_alsa_recover_from_poll() to merge common core from module-alsa-sink and module-alsa-source git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2294 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++-- src/modules/alsa-util.h | 2 ++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index d0565556..11367d95 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -974,7 +974,6 @@ void pa_alsa_redirect_errors_dec(void) { snd_lib_error_set_handler(NULL); } - void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = { @@ -1030,7 +1029,7 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info)); if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) { - pa_proplist_setf(p, "card", "%i", card); + pa_proplist_setf(p, "alsa.card", "%i", card); if (snd_card_get_name(card, &cn) >= 0) pa_proplist_sets(p, "alsa.card_name", cn); @@ -1046,3 +1045,51 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { else if (n) pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n); } + +int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) { + snd_pcm_state_t state; + int err; + + pa_assert(pcm); + + if (revents & POLLERR) + pa_log_warn("Got POLLERR from ALSA"); + if (revents & POLLNVAL) + pa_log_warn("Got POLLNVAL from ALSA"); + if (revents & POLLHUP) + pa_log_warn("Got POLLHUP from ALSA"); + + state = snd_pcm_state(pcm); + pa_log_warn("PCM state is %s", snd_pcm_state_name(state)); + + /* Try to recover from this error */ + + switch (state) { + + case SND_PCM_STATE_XRUN: + if ((err = snd_pcm_recover(pcm, -EPIPE, 1)) != 0) { + pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err)); + return -1; + } + break; + + case SND_PCM_STATE_SUSPENDED: + if ((err = snd_pcm_recover(pcm, -ESTRPIPE, 1)) != 0) { + pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err)); + return -1; + } + break; + + default: + + snd_pcm_drop(pcm); + + if ((err = snd_pcm_prepare(pcm)) < 0) { + pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err)); + return -1; + } + break; + } + + return 0; +} diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 03de61e2..5494b40d 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -90,4 +90,6 @@ void pa_alsa_redirect_errors_dec(void); void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info); +int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents); + #endif -- cgit From 4a1971a5351d49d3ff7fa33f386b579f558c2fd0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 02:46:19 +0000 Subject: if no latency was configure for a sink/source, fill in the max latency automatically git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2295 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 3 +++ src/pulsecore/source.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index e88e7d2b..452dab79 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1298,6 +1298,9 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) return 0; + if (usec == (pa_usec_t) -1) + usec = s->max_latency; + return usec; } diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 27279719..dab307e9 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -754,6 +754,9 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) { if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) return 0; + if (usec == (pa_usec_t) -1) + usec = s->max_latency; + return usec; } -- cgit From cdb077b6c040314ece12fd52fb41fb7b3f368909 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 02:47:05 +0000 Subject: if no timer was armed, we don't need to disarm it git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2296 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/rtpoll.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index bd1c43ad..734f344f 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -71,6 +71,7 @@ struct pa_rtpoll { int rtsig; sigset_t sigset_unblocked; timer_t timer; + pa_bool_t timer_armed; #ifdef __linux__ pa_bool_t dont_use_ppoll; #endif @@ -130,6 +131,7 @@ pa_rtpoll *pa_rtpoll_new(void) { p->rtsig = -1; sigemptyset(&p->sigset_unblocked); p->timer = (timer_t) -1; + p->timer_armed = FALSE; #endif @@ -465,14 +467,16 @@ static void update_timer(pa_rtpoll *p) { struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 }; sigset_t ss; - /* First disarm timer */ - memset(&its, 0, sizeof(its)); - pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); + if (p->timer_armed) { + /* First disarm timer */ + memset(&its, 0, sizeof(its)); + pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); - /* Remove a signal that might be waiting in the signal q */ - pa_assert_se(sigemptyset(&ss) == 0); - pa_assert_se(sigaddset(&ss, p->rtsig) == 0); - sigtimedwait(&ss, NULL, &ts); + /* Remove a signal that might be waiting in the signal q */ + pa_assert_se(sigemptyset(&ss) == 0); + pa_assert_se(sigaddset(&ss, p->rtsig) == 0); + sigtimedwait(&ss, NULL, &ts); + } /* And install the new timer */ if (p->timer_enabled) { @@ -487,6 +491,8 @@ static void update_timer(pa_rtpoll *p) { its.it_value.tv_nsec = 1; pa_assert_se(timer_settime(p->timer, TIMER_ABSTIME, &its, NULL) == 0); } + + p->timer_armed = p->timer_enabled; } #ifdef __linux__ -- cgit From 1adbe822b8e21542e0193e91376e73bbf7002fbe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 02:47:47 +0000 Subject: some beautification updates, show msec instead of usec everywhere git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2297 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/cli-text.c | 110 +++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 140334a1..f251a146 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -88,7 +89,7 @@ char *pa_client_list_to_string(pa_core *c) { client->driver); if (client->module) - pa_strbuf_printf(s, "\towner module: <%u>\n", client->module->index); + pa_strbuf_printf(s, "\towner module: %u\n", client->module->index); t = pa_proplist_to_string(client->proplist); pa_strbuf_printf(s, "\tproperties:\n%s", t); @@ -125,15 +126,15 @@ char *pa_sink_list_to_string(pa_core *c) { "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s\n" "\tstate: %s\n" - "\tvolume: <%s>\n" - "\tmute: <%i>\n" - "\tlatency: <%0.0f usec>\n" - "\tconfigured latency: <%0.0f usec> from range <%0.0f usec> .. <%0.0f usec>\n" - "\tmonitor source: <%u>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n" - "\tused by: <%u>\n" - "\tlinked by: <%u>\n", + "\tvolume: %s\n" + "\tmuted: %s\n" + "\tcurrent latency: %0.2f ms\n" + "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n" + "\tmonitor source: %u\n" + "\tsample spec: %s\n" + "\tchannel map: %s\n" + "\tused by: %u\n" + "\tlinked by: %u\n", c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ', sink->index, sink->name, @@ -146,9 +147,9 @@ char *pa_sink_list_to_string(pa_core *c) { sink->flags & PA_SINK_LATENCY ? "LATENCY " : "", state_table[pa_sink_get_state(sink)], pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)), - !!pa_sink_get_mute(sink), - (double) pa_sink_get_latency(sink), - (double) pa_sink_get_requested_latency(sink), (double) sink->min_latency, (double) sink->max_latency, + pa_yes_no(pa_sink_get_mute(sink)), + (double) pa_sink_get_latency(sink) / PA_USEC_PER_MSEC, + (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC, (double) sink->min_latency / PA_USEC_PER_MSEC, (double) sink->max_latency / PA_USEC_PER_MSEC, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX, pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map), @@ -156,7 +157,7 @@ char *pa_sink_list_to_string(pa_core *c) { pa_sink_linked_by(sink)); if (sink->module) - pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index); + pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index); t = pa_proplist_to_string(sink->proplist); pa_strbuf_printf(s, "\tproperties:\n%s", t); @@ -193,14 +194,14 @@ char *pa_source_list_to_string(pa_core *c) { "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s\n" "\tstate: %s\n" - "\tvolume: <%s>\n" - "\tmute: <%u>\n" - "\tlatency: <%0.0f usec>\n" - "\tconfigured latency: <%0.0f usec> from range <%0.0f usec> .. <%0.0f usec>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n" - "\tused by: <%u>\n" - "\tlinked by: <%u>\n", + "\tvolume: %s\n" + "\tmuted: %s\n" + "\tcurrent latency: %0.2f ms\n" + "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n" + "\tsample spec: %s\n" + "\tchannel map: %s\n" + "\tused by: %u\n" + "\tlinked by: %u\n", c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ', source->index, source->name, @@ -213,18 +214,18 @@ char *pa_source_list_to_string(pa_core *c) { source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", state_table[pa_source_get_state(source)], pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)), - !!pa_source_get_mute(source), - (double) pa_source_get_latency(source), - (double) pa_source_get_requested_latency(source), (double) source->min_latency, (double) source->max_latency, + pa_yes_no(pa_source_get_mute(source)), + (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC, + (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC, (double) source->min_latency / PA_USEC_PER_MSEC, (double) source->max_latency / PA_USEC_PER_MSEC, pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map), pa_source_used_by(source), pa_source_linked_by(source)); if (source->monitor_of) - pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); + pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index); if (source->module) - pa_strbuf_printf(s, "\tmodule: <%u>\n", source->module->index); + pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index); t = pa_proplist_to_string(source->proplist); pa_strbuf_printf(s, "\tproperties:\n%s", t); @@ -262,10 +263,10 @@ char *pa_source_output_list_to_string(pa_core *c) { "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s%s%s\n" "\tstate: %s\n" - "\tsource: <%u> '%s'\n" - "\tlatency: <%0.0f usec>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n" + "\tsource: %u <%s>\n" + "\tlatency: %0.2f ms\n" + "\tsample spec: %s\n" + "\tchannel map: %s\n" "\tresample method: %s\n", o->index, o->driver, @@ -279,14 +280,14 @@ char *pa_source_output_list_to_string(pa_core *c) { o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "", state_table[pa_source_output_get_state(o)], o->source->index, o->source->name, - (double) pa_source_output_get_latency(o), + (double) pa_source_output_get_latency(o) / PA_USEC_PER_MSEC, pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), pa_resample_method_to_string(pa_source_output_get_resample_method(o))); if (o->module) - pa_strbuf_printf(s, "\towner module: <%u>\n", o->module->index); + pa_strbuf_printf(s, "\towner module: %u\n", o->module->index); if (o->client) - pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME))); + pa_strbuf_printf(s, "\tclient: %u <%s>\n", o->client->index, pa_strnull(pa_proplist_gets(o->client->proplist, PA_PROP_APPLICATION_NAME))); t = pa_proplist_to_string(o->proplist); pa_strbuf_printf(s, "\tproperties:\n%s", t); @@ -324,12 +325,12 @@ char *pa_sink_input_list_to_string(pa_core *c) { "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s%s%s\n" "\tstate: %s\n" - "\tsink: <%u> '%s'\n" - "\tvolume: <%s>\n" - "\tmute: <%i>\n" - "\tlatency: <%0.0f usec>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n" + "\tsink: %u <%s>\n" + "\tvolume: %s\n" + "\tmuted: %s\n" + "\tlatency: %0.2f ms\n" + "\tsample spec: %s\n" + "\tchannel map: %s\n" "\tresample method: %s\n", i->index, i->driver, @@ -344,16 +345,16 @@ char *pa_sink_input_list_to_string(pa_core *c) { state_table[pa_sink_input_get_state(i)], i->sink->index, i->sink->name, pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), - !!pa_sink_input_get_mute(i), - (double) pa_sink_input_get_latency(i), + pa_yes_no(pa_sink_input_get_mute(i)), + (double) pa_sink_input_get_latency(i) / PA_USEC_PER_MSEC, pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); if (i->module) - pa_strbuf_printf(s, "\tmodule: <%u>\n", i->module->index); + pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index); if (i->client) - pa_strbuf_printf(s, "\tclient: <%u> '%s'\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME))); + pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME))); t = pa_proplist_to_string(i->proplist); pa_strbuf_printf(s, "\tproperties:\n%s", t); @@ -388,14 +389,14 @@ char *pa_scache_list_to_string(pa_core *c) { pa_strbuf_printf( s, " name: <%s>\n" - "\tindex: <%u>\n" - "\tsample spec: <%s>\n" - "\tchannel map: <%s>\n" - "\tlength: <%lu>\n" - "\tduration: <%0.1fs>\n" - "\tvolume: <%s>\n" + "\tindex: %u\n" + "\tsample spec: %s\n" + "\tchannel map: %s\n" + "\tlength: %lu\n" + "\tduration: %0.1f s\n" + "\tvolume: %s\n" "\tlazy: %s\n" - "\tfilename: %s\n", + "\tfilename: <%s>\n", e->name, e->index, ss, @@ -429,7 +430,12 @@ char *pa_autoload_list_to_string(pa_core *c) { while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) { pa_strbuf_printf( - s, " name: <%s>\n\ttype: <%s>\n\tindex: <%u>\n\tmodule_name: <%s>\n\targuments: <%s>\n", + s, + " name: <%s>\n" + "\ttype: %s\n" + "\tindex: %u\n" + "\tmodule_name: <%s>\n" + "\targuments: <%s>\n", e->name, e->type == PA_NAMEREG_SOURCE ? "source" : "sink", e->index, -- cgit From 6b4b95beedf08089ec0dc48dd152210e3bc75046 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 02:48:24 +0000 Subject: show configure latency metrics git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2298 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-native.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 7e86f117..cde113df 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -785,6 +785,10 @@ static playback_stream* playback_stream_new( tlength_usec = pa_bytes_to_usec(*tlength, &sink_input->sample_spec); minreq_usec = pa_bytes_to_usec(*minreq, &sink_input->sample_spec); + pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms", + (double) tlength_usec / PA_USEC_PER_MSEC, + (double) minreq_usec / PA_USEC_PER_MSEC); + if (adjust_latency) { /* So, the user asked us to adjust the latency of the stream @@ -878,6 +882,12 @@ static playback_stream* playback_stream_new( pa_idxset_put(c->output_streams, s, &s->index); + pa_log_info("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms", + ((double) pa_bytes_to_usec(*tlength, &sink_input->sample_spec) + (double) s->sink_latency) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(*tlength-*minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC, + (double) pa_bytes_to_usec(*minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC, + (double) s->sink_latency / PA_USEC_PER_MSEC); + pa_sink_input_put(s->sink_input); return s; } @@ -1048,8 +1058,6 @@ static void handle_seek(playback_stream *s, int64_t indexw) { if (u >= s->underrun) u = s->underrun; - pa_log("yeah! ready to rock"); - /* We just ended an underrun, let's ask the sink * to rewrite */ s->sink_input->thread_info.ignore_rewind = TRUE; -- cgit From 5e6aacdbe2df444ab9708ce3b007bece32d2cb93 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 22 Apr 2008 02:50:55 +0000 Subject: * don't increase tsched_watermark on underrun without limits * fix the watermark when we change the latency * fix latency measurement * move rewinding code into its own function * make use of new function pa_alsa_recover_from_poll() were applicable git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2299 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 207 +++++++++++++++++---------------------- src/modules/module-alsa-source.c | 58 ++--------- 2 files changed, 98 insertions(+), 167 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index c3ae6e65..665dbdee 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -92,7 +92,7 @@ static const char* const valid_modargs[] = { }; #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (10*PA_USEC_PER_SEC) /* 10s */ +#define DEFAULT_TSCHED_BUFFER_USEC (5*PA_USEC_PER_SEC) /* 5s */ #define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ struct userdata { @@ -134,6 +134,16 @@ struct userdata { snd_pcm_sframes_t avail_min_frames; }; +static void fix_tsched_watermark(struct userdata *u) { + size_t max_use; + pa_assert(u); + + max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size; + + if (u->tsched_watermark >= max_use/2) + u->tsched_watermark = max_use/2; +} + static int mmap_write(struct userdata *u) { int work_done = 0; @@ -189,12 +199,10 @@ static int mmap_write(struct userdata *u) { pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); if (left_to_play <= 0 && !u->first) { - u->tsched_watermark *=2; - - if (u->tsched_watermark >= u->hwbuf_size) - u->tsched_watermark = u->hwbuf_size-u->frame_size; - - pa_log_notice("Underrun! Increasing wakeup watermark to %0.2f", (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + u->tsched_watermark *= 2; + fix_tsched_watermark(u); + pa_log_notice("Underrun! Increasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); } frames = n = n - u->hwbuf_unused_frames; @@ -407,13 +415,17 @@ static void update_smoother(struct userdata *u) { static pa_usec_t sink_get_latency(struct userdata *u) { pa_usec_t r = 0; int64_t delay; + pa_usec_t now1, now2; pa_assert(u); - delay = u->frame_index - pa_smoother_get(u->smoother, pa_rtclock_usec()); + now1 = pa_rtclock_usec(); + now2 = pa_smoother_get(u->smoother, now1); + + delay = (int64_t) pa_bytes_to_usec(u->frame_index * u->frame_size, &u->sink->sample_spec) - now2; if (delay > 0) - r = pa_bytes_to_usec(delay * u->frame_size, &u->sink->sample_spec); + r = (pa_usec_t) delay; if (u->memchunk.memblock) r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec); @@ -523,6 +535,8 @@ static int update_sw_params(struct userdata *u) { u->hwbuf_unused_frames = PA_LIKELY(b < u->hwbuf_size) ? ((u->hwbuf_size - b) / u->frame_size) : 0; + + fix_tsched_watermark(u); } pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames); @@ -815,12 +829,66 @@ static int sink_set_mute_cb(pa_sink *s) { static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u = s->userdata; - pa_assert(u); update_sw_params(u); } +static int process_rewind(struct userdata *u) { + snd_pcm_sframes_t unused; + size_t rewind_nbytes, unused_nbytes, limit_nbytes; + pa_assert(u); + + rewind_nbytes = u->sink->thread_info.rewind_nbytes; + u->sink->thread_info.rewind_nbytes = 0; + + pa_assert(rewind_nbytes > 0); + pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); + + snd_pcm_hwsync(u->pcm_handle); + if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) { + pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused)); + return -1; + } + + unused_nbytes = u->tsched_watermark + (size_t) unused * u->frame_size; + + if (u->hwbuf_size > unused_nbytes) + limit_nbytes = u->hwbuf_size - unused_nbytes; + else + limit_nbytes = 0; + + if (rewind_nbytes > limit_nbytes) + rewind_nbytes = limit_nbytes; + + if (rewind_nbytes > 0) { + snd_pcm_sframes_t in_frames, out_frames; + + pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes); + + in_frames = (snd_pcm_sframes_t) rewind_nbytes / u->frame_size; + pa_log_debug("before: %lu", (unsigned long) in_frames); + if ((out_frames = snd_pcm_rewind(u->pcm_handle, in_frames)) < 0) { + pa_log("snd_pcm_rewind() failed: %s", snd_strerror(out_frames)); + return -1; + } + pa_log_debug("after: %lu", (unsigned long) out_frames); + + rewind_nbytes = out_frames * u->frame_size; + + if (rewind_nbytes <= 0) + pa_log_info("Tried rewind, but was apparently not possible."); + else { + u->frame_index -= out_frames; + pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); + pa_sink_process_rewind(u->sink, rewind_nbytes); + } + } else + pa_log_debug("Mhmm, actually there is nothing to rewind."); + + return 0; +} + static void thread_func(void *userdata) { struct userdata *u = userdata; @@ -843,78 +911,17 @@ static void thread_func(void *userdata) { if (PA_SINK_OPENED(u->sink->thread_info.state)) { int work_done = 0; - if (u->sink->thread_info.rewind_nbytes > 0) { - snd_pcm_sframes_t unused; - size_t rewind_nbytes, unused_nbytes, limit_nbytes; - - rewind_nbytes = u->sink->thread_info.rewind_nbytes; - u->sink->thread_info.rewind_nbytes = 0; - - pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); - - snd_pcm_hwsync(u->pcm_handle); - if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) { - pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused)); + if (u->sink->thread_info.rewind_nbytes > 0) + if (process_rewind(u) < 0) goto fail; - } - - unused_nbytes = u->tsched_watermark + (size_t) unused * u->frame_size; - - if (u->hwbuf_size > unused_nbytes) - limit_nbytes = u->hwbuf_size - unused_nbytes; - else - limit_nbytes = 0; - - if (rewind_nbytes > limit_nbytes) - rewind_nbytes = limit_nbytes; - - if (rewind_nbytes > 0) { - snd_pcm_sframes_t in_frames, out_frames; - - pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes); - - in_frames = (snd_pcm_sframes_t) rewind_nbytes / u->frame_size; - pa_log_debug("before: %lu", (unsigned long) in_frames); - if ((out_frames = snd_pcm_rewind(u->pcm_handle, in_frames)) < 0) { - pa_log("snd_pcm_rewind() failed: %s", snd_strerror(out_frames)); - goto fail; - } - pa_log_debug("after: %lu", (unsigned long) out_frames); - - if (out_frames > in_frames) { - snd_pcm_sframes_t sfix; - pa_log("FUCK, device rewound %lu frames more than we wanted. What a mess!", (unsigned long) (out_frames-in_frames)); - - if ((sfix = snd_pcm_forward(u->pcm_handle, out_frames-in_frames)) < 0) { - pa_log("snd_pcm_forward() failed: %s", snd_strerror(sfix)); - goto fail; - } - - pa_log("Could fix by %lu", (unsigned long) sfix); - out_frames -= sfix; - } - - rewind_nbytes = out_frames * u->frame_size; - - if (rewind_nbytes <= 0) - pa_log_info("Tried rewind, but was apparently not possible."); - else { - u->frame_index -= out_frames; - pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); - pa_sink_process_rewind(u->sink, rewind_nbytes); - } - } else - pa_log_debug("Mhmm, actually there is nothing to rewind."); - } + if (u->use_mmap) + work_done = mmap_write(u); + else + work_done = unix_write(u); - if (u->use_mmap) { - if ((work_done = mmap_write(u)) < 0) - goto fail; - } else { - if ((work_done = unix_write(u)) < 0) - goto fail; - } + if (work_done < 0) + goto fail; /* pa_log_debug("work_done = %i", work_done); */ @@ -979,46 +986,8 @@ static void thread_func(void *userdata) { } if (revents & (POLLERR|POLLNVAL|POLLHUP)) { - snd_pcm_state_t state; - - if (revents & POLLERR) - pa_log_warn("Got POLLERR from ALSA"); - if (revents & POLLNVAL) - pa_log_warn("Got POLLNVAL from ALSA"); - if (revents & POLLHUP) - pa_log_warn("Got POLLHUP from ALSA"); - - state = snd_pcm_state(u->pcm_handle); - pa_log_warn("PCM state is %s", snd_pcm_state_name(state)); - - /* Try to recover from this error */ - - switch (state) { - - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err)); - goto fail; - } - break; - - case SND_PCM_STATE_SUSPENDED: - if ((err = snd_pcm_recover(u->pcm_handle, -ESTRPIPE, 1)) != 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err)); - goto fail; - } - break; - - default: - - snd_pcm_drop(u->pcm_handle); - - if ((err = snd_pcm_prepare(u->pcm_handle)) < 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err)); - goto fail; - } - break; - } + if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0) + goto fail; u->first = TRUE; } diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index a0677f8d..bcb3c2d0 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -93,8 +93,8 @@ static const char* const valid_modargs[] = { }; #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) -#define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC) +#define DEFAULT_TSCHED_BUFFER_USEC (5*PA_USEC_PER_SEC) +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) struct userdata { pa_core *core; @@ -749,14 +749,13 @@ static void thread_func(void *userdata) { if (PA_SOURCE_OPENED(u->source->thread_info.state)) { int work_done = 0; - if (u->use_mmap) { - if ((work_done = mmap_read(u)) < 0) - goto fail; + if (u->use_mmap) + work_done = mmap_read(u); + else + work_done = unix_read(u); - } else { - if ((work_done = unix_read(u) < 0)) - goto fail; - } + if (work_done < 0) + goto fail; if (work_done) update_smoother(u); @@ -807,46 +806,9 @@ static void thread_func(void *userdata) { } if (revents & (POLLERR|POLLNVAL|POLLHUP)) { - snd_pcm_state_t state; - - if (revents & POLLERR) - pa_log_warn("Got POLLERR from ALSA"); - if (revents & POLLNVAL) - pa_log_warn("Got POLLNVAL from ALSA"); - if (revents & POLLHUP) - pa_log_warn("Got POLLHUP from ALSA"); - - state = snd_pcm_state(u->pcm_handle); - pa_log_warn("PCM state is %s", snd_pcm_state_name(state)); - - /* Try to recover from this error */ - switch (state) { - - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err)); - goto fail; - } - break; - - case SND_PCM_STATE_SUSPENDED: - if ((err = snd_pcm_recover(u->pcm_handle, -ESTRPIPE, 1)) != 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err)); - goto fail; - } - break; - - default: - - snd_pcm_drop(u->pcm_handle); - - if ((err = snd_pcm_prepare(u->pcm_handle)) < 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err)); - goto fail; - } - break; - } + if (pa_alsa_recover_from_poll(u->pcm_handle, revents)) + goto fail; snd_pcm_start(u->pcm_handle); } -- cgit From 3c8e83fb3ebd4e1e6496e047cbf4c357550631b4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:10:34 +0000 Subject: do not fix automatic buffer attrs anymore, the new protocol version doesn't need this anymore and it creates more problems than it solves. Also drop the initial timing info query. Correct programs shouldn't depend on it anyway git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2300 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/internal.h | 1 - src/pulse/stream.c | 53 ++++------------------------------------------------ 2 files changed, 4 insertions(+), 50 deletions(-) diff --git a/src/pulse/internal.h b/src/pulse/internal.h index d988e1a7..f15c69c3 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -116,7 +116,6 @@ struct pa_stream { uint32_t stream_index; pa_stream_direction_t direction; pa_stream_state_t state; - pa_bool_t buffer_attr_not_ready, timing_info_not_ready; uint32_t requested_bytes; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index e67f24c0..ccbabb55 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -68,8 +68,6 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa s->context = c; s->mainloop = c->mainloop; - s->buffer_attr_not_ready = s->timing_info_not_ready = FALSE; - s->read_callback = NULL; s->read_userdata = NULL; s->write_callback = NULL; @@ -527,9 +525,6 @@ static void create_stream_complete(pa_stream *s) { pa_assert(PA_REFCNT_VALUE(s) >= 1); pa_assert(s->state == PA_STREAM_CREATING); - if (s->buffer_attr_not_ready || s->timing_info_not_ready) - return; - pa_stream_set_state(s, PA_STREAM_READY); if (s->requested_bytes > 0 && s->write_callback) @@ -562,7 +557,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s attr->tlength = pa_bytes_per_second(ss)*2; /* 2s of buffering */ if (!attr->minreq <= 0) - attr->minreq = (9*attr->tlength)/10; /* Ask for more data when there are only 200ms left in the playback buffer */ + attr->minreq = (2*attr->tlength)/10; /* Ask for more data when there are only 200ms left in the playback buffer */ if (!attr->prebuf) attr->prebuf = attr->tlength; /* Start to play only when the playback is fully filled up once */ @@ -644,25 +639,6 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED s->device_name = pa_xstrdup(dn); s->suspended = suspended; - if (!s->manual_buffer_attr && pa_bytes_per_second(&ss) != pa_bytes_per_second(&s->sample_spec)) { - pa_buffer_attr attr; - pa_operation *o; - - memset(&attr, 0, sizeof(attr)); - automatic_buffer_attr(s, &attr, &ss); - - /* If we need to update the buffer metrics, we wait for - * the the OK for that call before we go to - * PA_STREAM_READY */ - - s->state = PA_STREAM_READY; - pa_assert_se(o = pa_stream_set_buffer_attr(s, &attr, NULL, NULL)); - pa_operation_unref(o); - s->state = PA_STREAM_CREATING; - - s->buffer_attr_not_ready = TRUE; - } - s->channel_map = cm; s->sample_spec = ss; } @@ -703,19 +679,6 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED s->channel_valid = 1; pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); - if (s->direction != PA_STREAM_UPLOAD && s->flags & PA_STREAM_AUTO_TIMING_UPDATE) { - - /* If automatic timing updates are active, we wait for the - * first timing update before going to PA_STREAM_READY - * state */ - - s->state = PA_STREAM_READY; - request_auto_timing_update(s, 1); - s->state = PA_STREAM_CREATING; - - s->timing_info_not_ready = TRUE; - } - create_stream_complete(s); finish: @@ -1212,12 +1175,6 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, } } - /* First, let's complete the initialization, if necessary. */ - if (o->stream->state == PA_STREAM_CREATING) { - o->stream->timing_info_not_ready = FALSE; - create_stream_complete(o->stream); - } - if (o->stream->latency_update_callback) o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata); @@ -1777,11 +1734,6 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, o->stream->manual_buffer_attr = TRUE; } - if (o->stream->state == PA_STREAM_CREATING) { - o->stream->buffer_attr_not_ready = FALSE; - create_stream_complete(o->stream); - } - if (o->callback) { pa_stream_success_cb_t cb = (pa_stream_success_cb_t) o->callback; cb(o->stream, success, o->userdata); @@ -1826,6 +1778,9 @@ pa_operation* pa_stream_set_buffer_attr(pa_stream *s, const pa_buffer_attr *attr else pa_tagstruct_putu32(t, attr->fragsize); + if (s->context->version >= 13) + pa_tagstruct_put_boolean(t, !!(s->flags & PA_STREAM_ADJUST_LATENCY)); + pa_pstream_send_tagstruct(s->context->pstream, t); pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, stream_set_buffer_attr_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); -- cgit From af03dd4e4fe668c988e1d9e781212763d3e9d56e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:11:14 +0000 Subject: drop a misplaced newline git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2301 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/resampler.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 3d7e5364..d645639c 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -1442,7 +1442,6 @@ static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned i pa_assert(o_index * fz < pa_memblock_get_length(output->memblock)); - if (r->work_format == PA_SAMPLE_S16NE) { unsigned i, c; int16_t *s = (int16_t*) ((uint8_t*) src + fz * j); -- cgit From ed0af46e69a207249bfbade2babfbc93b3f89fc9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:11:57 +0000 Subject: unify code that fixes up buffering metrics git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2302 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-native.c | 345 ++++++++++++++++++++++------------------ 1 file changed, 188 insertions(+), 157 deletions(-) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index cde113df..ca14b955 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -470,11 +470,65 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i return 0; } +static void fix_record_buffer_attr_pre(record_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *fragsize) { + pa_assert(s); + pa_assert(maxlength); + pa_assert(fragsize); + + if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) + *maxlength = MAX_MEMBLOCKQ_LENGTH; + + if (*fragsize <= 0) + *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec); + + if (adjust_latency) { + pa_usec_t fragsize_usec; + + /* So, the user asked us to adjust the latency according to + * the what the source can provide. Half the latency will be + * spent on the hw buffer, half of it in the async buffer + * queue we maintain for each client. */ + + fragsize_usec = pa_bytes_to_usec(*fragsize, &s->source_output->sample_spec); + + s->source_latency = pa_source_output_set_requested_latency(s->source_output, fragsize_usec/2); + + if (fragsize_usec >= s->source_latency*2) + fragsize_usec -= s->source_latency; + else + fragsize_usec = s->source_latency; + + *fragsize = pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec); + } +} + +static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, uint32_t *fragsize) { + size_t base; + + pa_assert(s); + pa_assert(maxlength); + pa_assert(fragsize); + + *maxlength = pa_memblockq_get_maxlength(s->memblockq); + + base = pa_frame_size(&s->source_output->sample_spec); + + s->fragment_size = (*fragsize/base)*base; + if (s->fragment_size <= 0) + s->fragment_size = base; + + if (s->fragment_size > *maxlength) + s->fragment_size = *maxlength; + + *fragsize = s->fragment_size; +} + static record_stream* record_stream_new( connection *c, pa_source *source, pa_sample_spec *ss, pa_channel_map *map, + pa_bool_t peak_detect, uint32_t *maxlength, uint32_t *fragsize, pa_source_output_flags_t flags, @@ -501,6 +555,8 @@ static record_stream* record_stream_new( data.source = source; pa_source_output_new_data_set_sample_spec(&data, ss); pa_source_output_new_data_set_channel_map(&data, map); + if (peak_detect) + data.resample_method = PA_RESAMPLER_PEAKS; source_output = pa_source_output_new(c->protocol->core, &data, flags); @@ -522,30 +578,7 @@ static record_stream* record_stream_new( s->source_output->suspend = source_output_suspend_cb; s->source_output->userdata = s; - if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) - *maxlength = MAX_MEMBLOCKQ_LENGTH; - if (*fragsize <= 0) - *fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &source_output->sample_spec); - - if (adjust_latency) { - pa_usec_t fragsize_usec; - - /* So, the user asked us to adjust the latency according to - * the what the source can provide. Half the latency will be - * spent on the hw buffer, half of it in the async buffer - * queue we maintain for each client. */ - - fragsize_usec = pa_bytes_to_usec(*fragsize, &source_output->sample_spec); - - s->source_latency = pa_source_output_set_requested_latency(source_output, fragsize_usec/2); - - if (fragsize_usec >= s->source_latency*2) - fragsize_usec -= s->source_latency; - else - fragsize_usec = s->source_latency; - - *fragsize = pa_usec_to_bytes(fragsize_usec, &source_output->sample_spec); - } + fix_record_buffer_attr_pre(s, adjust_latency, maxlength, fragsize); s->memblockq = pa_memblockq_new( 0, @@ -557,16 +590,7 @@ static record_stream* record_stream_new( 0, NULL); - *maxlength = pa_memblockq_get_maxlength(s->memblockq); - - s->fragment_size = (*fragsize/base)*base; - if (s->fragment_size <= 0) - s->fragment_size = base; - - if (s->fragment_size > *maxlength) - s->fragment_size = *maxlength; - - *fragsize = s->fragment_size; + fix_record_buffer_attr_post(s, maxlength, fragsize); *ss = s->source_output->sample_spec; *map = s->source_output->channel_map; @@ -674,6 +698,114 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, return 0; } +static void fix_playback_buffer_attr_pre(playback_stream *s, pa_bool_t adjust_latency, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) { + size_t frame_size; + pa_usec_t tlength_usec, minreq_usec, sink_usec; + + pa_assert(s); + pa_assert(maxlength); + pa_assert(tlength); + pa_assert(prebuf); + pa_assert(minreq); + + if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) + *maxlength = MAX_MEMBLOCKQ_LENGTH; + if (*tlength <= 0) + *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); + if (*minreq <= 0) + *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec); + + frame_size = pa_frame_size(&s->sink_input->sample_spec); + if (*minreq <= 0) + *minreq = frame_size; + if (*tlength < *minreq+frame_size) + *tlength = *minreq+frame_size; + + tlength_usec = pa_bytes_to_usec(*tlength, &s->sink_input->sample_spec); + minreq_usec = pa_bytes_to_usec(*minreq, &s->sink_input->sample_spec); + + pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms", + (double) tlength_usec / PA_USEC_PER_MSEC, + (double) minreq_usec / PA_USEC_PER_MSEC); + + if (adjust_latency) { + + /* So, the user asked us to adjust the latency of the stream + * buffer according to the what the sink can provide. The + * tlength passed in shall be the overall latency. Roughly + * half the latency will be spent on the hw buffer, the other + * half of it in the async buffer queue we maintain for each + * client. In between we'll have a safety space of size + * 2*minreq. Why the 2*minreq? When the hw buffer is completey + * empty and needs to be filled, then our buffer must have + * enough data to fulfill this request immediatly and thus + * have at least the same tlength as the size of the hw + * buffer. It additionally needs space for 2 times minreq + * because if the buffer ran empty and a partial fillup + * happens immediately on the next iteration we need to be + * able to fulfill it and give the application also minreq + * time to fill it up again for the next request Makes 2 times + * minreq in plus.. */ + + if (tlength_usec > minreq_usec*2) + sink_usec = (tlength_usec - minreq_usec*2)/2; + else + sink_usec = 0; + + } else { + + /* Ok, the user didn't ask us to adjust the latency, but we + * still need to make sure that the parameters from the user + * do make sense. */ + + if (tlength_usec > minreq_usec*2) + sink_usec = (tlength_usec - minreq_usec*2); + else + sink_usec = 0; + } + + s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec); + + if (adjust_latency) { + /* Ok, we didn't necessarily get what we were asking for, so + * let's subtract from what we asked for for the remaining + * buffer space */ + + if (tlength_usec >= s->sink_latency) + tlength_usec -= s->sink_latency; + } + + if (tlength_usec < s->sink_latency + 2*minreq_usec) + tlength_usec = s->sink_latency + 2*minreq_usec; + + *tlength = pa_usec_to_bytes(tlength_usec, &s->sink_input->sample_spec); + *minreq = pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec); + + if (*minreq <= 0) { + *minreq += frame_size; + *tlength += frame_size*2; + } + + if (*tlength <= *minreq) + *tlength = *minreq*2 + frame_size; + + if (*prebuf <= 0) + *prebuf = *tlength; +} + +static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlength, uint32_t *tlength, uint32_t* prebuf, uint32_t* minreq) { + pa_assert(s); + pa_assert(maxlength); + pa_assert(tlength); + pa_assert(prebuf); + pa_assert(minreq); + + *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); + *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); + *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); + *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); +} + static playback_stream* playback_stream_new( connection *c, pa_sink *sink, @@ -697,8 +829,6 @@ static playback_stream* playback_stream_new( uint32_t idx; int64_t start_index; pa_sink_input_new_data data; - pa_usec_t tlength_usec, minreq_usec, sink_usec; - size_t frame_size; pa_assert(c); pa_assert(ss); @@ -769,90 +899,7 @@ static playback_stream* playback_stream_new( start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0; - if (*maxlength <= 0 || *maxlength > MAX_MEMBLOCKQ_LENGTH) - *maxlength = MAX_MEMBLOCKQ_LENGTH; - if (*tlength <= 0) - *tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &sink_input->sample_spec); - if (*minreq <= 0) - *minreq = pa_usec_to_bytes(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &sink_input->sample_spec); - - frame_size = pa_frame_size(&sink_input->sample_spec); - if (*minreq <= 0) - *minreq = frame_size; - if (*tlength < *minreq+frame_size) - *tlength = *minreq+frame_size; - - tlength_usec = pa_bytes_to_usec(*tlength, &sink_input->sample_spec); - minreq_usec = pa_bytes_to_usec(*minreq, &sink_input->sample_spec); - - pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms", - (double) tlength_usec / PA_USEC_PER_MSEC, - (double) minreq_usec / PA_USEC_PER_MSEC); - - if (adjust_latency) { - - /* So, the user asked us to adjust the latency of the stream - * buffer according to the what the sink can provide. The - * tlength passed in shall be the overall latency. Roughly - * half the latency will be spent on the hw buffer, the other - * half of it in the async buffer queue we maintain for each - * client. In between we'll have a safety space of size - * 2*minreq. Why the 2*minreq? When the hw buffer is completey - * empty and needs to be filled, then our buffer must have - * enough data to fulfill this request immediatly and thus - * have at least the same tlength as the size of the hw - * buffer. It additionally needs space for 2 times minreq - * because if the buffer ran empty and a partial fillup - * happens immediately on the next iteration we need to be - * able to fulfill it and give the application also minreq - * time to fill it up again for the next request Makes 2 times - * minreq in plus.. */ - - if (tlength_usec > minreq_usec*2) - sink_usec = (tlength_usec - minreq_usec*2)/2; - else - sink_usec = 0; - - } else { - - /* Ok, the user didn't ask us to adjust the latency, but we - * still need to make sure that the parameters from the user - * do make sense. */ - - if (tlength_usec > minreq_usec*2) - sink_usec = (tlength_usec - minreq_usec*2); - else - sink_usec = 0; - } - - s->sink_latency = pa_sink_input_set_requested_latency(sink_input, sink_usec); - - if (adjust_latency) { - /* Ok, we didn't necessarily get what we were asking for, so - * let's subtract from what we asked for for the remaining - * buffer space */ - - if (tlength_usec >= s->sink_latency) - tlength_usec -= s->sink_latency; - } - - if (tlength_usec < s->sink_latency + 2*minreq_usec) - tlength_usec = s->sink_latency + 2*minreq_usec; - - *tlength = pa_usec_to_bytes(tlength_usec, &sink_input->sample_spec); - *minreq = pa_usec_to_bytes(minreq_usec, &sink_input->sample_spec); - - if (*minreq <= 0) { - *minreq += frame_size; - *tlength += frame_size*2; - } - - if (*tlength <= *minreq) - *tlength = *minreq*2 + frame_size; - - if (*prebuf <= 0) - *prebuf = *tlength; - + fix_playback_buffer_attr_pre(s, adjust_latency, maxlength, tlength, prebuf, minreq); pa_sink_input_get_silence(sink_input, &silence); s->memblockq = pa_memblockq_new( @@ -866,17 +913,14 @@ static playback_stream* playback_stream_new( &silence); pa_memblock_unref(silence.memblock); + fix_playback_buffer_attr_post(s, maxlength, tlength, prebuf, minreq); - *maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); - *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); - *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); - *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq); *ss = s->sink_input->sample_spec; *map = s->sink_input->channel_map; - s->minreq = pa_memblockq_get_minreq(s->memblockq); + s->minreq = *minreq; pa_atomic_store(&s->missing, 0); s->drain_request = FALSE; @@ -1671,7 +1715,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ fix_channels = FALSE, no_move = FALSE, variable_rate = FALSE, - adjust_latency = FALSE; + adjust_latency = FALSE, + peak_detect = FALSE; pa_source_output_flags_t flags = 0; pa_proplist *p; @@ -1720,7 +1765,8 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ if (c->version >= 13) { - if (pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || + if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 || + pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || pa_tagstruct_get_proplist(t, p) < 0) { protocol_error(c); pa_proplist_free(p); @@ -1761,7 +1807,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0); - s = record_stream_new(c, source, &ss, &map, &maxlength, &fragment_size, flags, p, adjust_latency); + s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID); @@ -2940,6 +2986,7 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) { playback_stream *s; + pa_bool_t adjust_latency = FALSE; s = pa_idxset_get_by_index(c->output_streams, idx); CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); @@ -2952,34 +2999,28 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u PA_TAG_U32, &prebuf, PA_TAG_U32, &minreq, PA_TAG_INVALID) < 0 || + (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) || !pa_tagstruct_eof(t)) { protocol_error(c); return; } - if (maxlength <= 0 || maxlength > MAX_MEMBLOCKQ_LENGTH) - maxlength = MAX_MEMBLOCKQ_LENGTH; - if (tlength <= 0) - tlength = pa_usec_to_bytes(DEFAULT_TLENGTH_MSEC*1000, &s->sink_input->sample_spec); - if (minreq <= 0) - minreq = (tlength*9)/10; - if (prebuf <= 0) - prebuf = tlength; - + fix_playback_buffer_attr_pre(s, adjust_latency, &maxlength, &tlength, &prebuf, &minreq); pa_memblockq_set_maxlength(s->memblockq, maxlength); pa_memblockq_set_tlength(s->memblockq, tlength); pa_memblockq_set_prebuf(s->memblockq, prebuf); pa_memblockq_set_minreq(s->memblockq, minreq); + fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq); reply = reply_new(tag); - pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq)); - pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_tlength(s->memblockq)); - pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_prebuf(s->memblockq)); - pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_minreq(s->memblockq)); + pa_tagstruct_putu32(reply, maxlength); + pa_tagstruct_putu32(reply, tlength); + pa_tagstruct_putu32(reply, prebuf); + pa_tagstruct_putu32(reply, minreq); } else { record_stream *s; - size_t base; + pa_bool_t adjust_latency = FALSE; pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR); s = pa_idxset_get_by_index(c->record_streams, idx); @@ -2990,29 +3031,19 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u PA_TAG_U32, &maxlength, PA_TAG_U32, &fragsize, PA_TAG_INVALID) < 0 || + (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) || !pa_tagstruct_eof(t)) { protocol_error(c); return; } - if (maxlength <= 0 || maxlength > MAX_MEMBLOCKQ_LENGTH) - maxlength = MAX_MEMBLOCKQ_LENGTH; - if (fragsize <= 0) - fragsize = pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*1000, &s->source_output->sample_spec); - + fix_record_buffer_attr_pre(s, adjust_latency, &maxlength, &fragsize); pa_memblockq_set_maxlength(s->memblockq, maxlength); - - base = pa_frame_size(&s->source_output->sample_spec); - s->fragment_size = (fragsize/base)*base; - if (s->fragment_size <= 0) - s->fragment_size = base; - - if (s->fragment_size > pa_memblockq_get_maxlength(s->memblockq)) - s->fragment_size = pa_memblockq_get_maxlength(s->memblockq); + fix_record_buffer_attr_post(s, &maxlength, &fragsize); reply = reply_new(tag); - pa_tagstruct_putu32(reply, (uint32_t) pa_memblockq_get_maxlength(s->memblockq)); - pa_tagstruct_putu32(reply, s->fragment_size); + pa_tagstruct_putu32(reply, maxlength); + pa_tagstruct_putu32(reply, fragsize); } pa_pstream_send_tagstruct(c->pstream, reply); -- cgit From 3f57d3aab2ded1c730ac8625e8ca6242d4445954 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:12:46 +0000 Subject: add new function pa_alsa_build_pollfd() to alsa-util to unify a bit more common code from the sink and the source git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2303 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 11367d95..0c4c020b 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -1093,3 +1093,27 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) { return 0; } + +pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) { + int n, err; + struct pollfd *pollfd; + pa_rtpoll_item *item; + + pa_assert(pcm); + + if ((n = snd_pcm_poll_descriptors_count(pcm)) < 0) { + pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n)); + return NULL; + } + + item = pa_rtpoll_item_new(rtpoll, PA_RTPOLL_NEVER, n); + pollfd = pa_rtpoll_item_get_pollfd(item, NULL); + + if ((err = snd_pcm_poll_descriptors(pcm, pollfd, n)) < 0) { + pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err)); + pa_rtpoll_item_free(item); + return NULL; + } + + return item; +} -- cgit From 998ed8ede1f4ff700b36864e58e3ed3ae284e486 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:13:11 +0000 Subject: add missing header definitions for last commit git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2304 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h index 5494b40d..442c2645 100644 --- a/src/modules/alsa-util.h +++ b/src/modules/alsa-util.h @@ -32,6 +32,8 @@ #include #include +#include + typedef struct pa_alsa_fdlist pa_alsa_fdlist; struct pa_alsa_fdlist *pa_alsa_fdlist_new(void); @@ -92,4 +94,6 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info); int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents); +pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll); + #endif -- cgit From 0b183fbdc6c83a0fe0f19343c72a524f0bb2ca9f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:14:25 +0000 Subject: respect the resampler's maximum block size to avoid that we get kicked out of the memory pool due to resampling. actually drop data from the delay queue after we used it git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2305 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/source-output.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 9c75b39c..de543a57 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -359,7 +359,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o) { /* Called from thread context */ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { size_t length; - size_t limit; + size_t limit, mbs = 0; pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); @@ -396,6 +396,12 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { else { pa_memchunk rchunk; + if (mbs == 0) + mbs = pa_resampler_max_block_size(o->thread_info.resampler); + + if (qchunk.length > mbs) + qchunk.length = mbs; + pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk); if (rchunk.length > 0) @@ -405,6 +411,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { } pa_memblock_unref(qchunk.memblock); + pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length); } } -- cgit From f2dffb717d592fb02f009f235e8a05763c500684 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:17:17 +0000 Subject: pa_bool_t'ization git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2306 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/asyncq-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c index 09b20047..0e10ed89 100644 --- a/src/tests/asyncq-test.c +++ b/src/tests/asyncq-test.c @@ -44,7 +44,7 @@ static void producer(void *_q) { pa_asyncq_push(q, PA_UINT_TO_PTR(i+1), 1); } - pa_asyncq_push(q, PA_UINT_TO_PTR(-1), 1); + pa_asyncq_push(q, PA_UINT_TO_PTR(-1), TRUE); printf("pushed end\n"); } @@ -56,7 +56,7 @@ static void consumer(void *_q) { sleep(1); for (i = 0;; i++) { - p = pa_asyncq_pop(q, 1); + p = pa_asyncq_pop(q, TRUE); if (p == PA_UINT_TO_PTR(-1)) break; -- cgit From a197644ea2cac5e35f2ca6d3d2af149ebedc13ba Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:19:45 +0000 Subject: add new tool 'stripnul' which can be used to drop leading zeros from a file which is useful to do byte-by-byte comparison of what goes in and comes out of PA git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2307 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/stripnul.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/tests/stripnul.c diff --git a/src/tests/stripnul.c b/src/tests/stripnul.c new file mode 100644 index 00000000..2f87e877 --- /dev/null +++ b/src/tests/stripnul.c @@ -0,0 +1,72 @@ +/* $Id$ */ + +/*** + This file is part of PulseAudio. + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include +#include + +int main(int argc, char *argv[]) { + FILE *i, *o; + size_t granularity; + pa_bool_t found; + uint8_t *zero; + + pa_assert_se(argc >= 2); + pa_assert_se((granularity = atoi(argv[1])) >= 1); + pa_assert_se((i = (argc >= 3) ? fopen(argv[2], "r") : stdin)); + pa_assert_se((o = (argc >= 4) ? fopen(argv[3], "w") : stdout)); + + zero = pa_xmalloc0(granularity); + + for (;;) { + uint8_t buffer[16*1024], *p; + size_t k; + + k = fread(buffer, granularity, sizeof(buffer)/granularity, i); + + if (k <= 0) + break; + + if (found) + pa_assert_se(fwrite(buffer, granularity, k, o) == k); + else { + for (p = buffer; (p-buffer)/granularity < k; p += granularity) + if (memcmp(p, zero, granularity)) { + size_t left; + found = TRUE; + left = k - (p-buffer)/granularity; + pa_assert_se(fwrite(p, granularity, left, o) == left); + break; + } + } + } + + fflush(o); + + return 0; +} -- cgit From 76031df4a4a156b7a6a9723b108bfdb37521ef7c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:26:48 +0000 Subject: Big pile of interdependant changes: * Fix a deadlock when an asyncq overflows and an RT thread needed to wait until space became available again while the main thread was waiting for a operation to complete and thus didn't free any new items. Now, if the asyncq overflows, queue those items temporarily, and return immediately. Then, when the queue becomes writable again, flush it. * Modify pa_thread_mq_init() to also set up pa_rtpoll events properly for the MQ * Some more pa_bool_t'ization * Unify more common code between alsa-sink and alsa-source * The upper limit for the tsched watermark is max_use minus one frame * make module-alsa-source work * make the alsa modules use pa_alsa_build_pollfd() now * fix detection of dB scale for alsa-source git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2308 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 248 ++++++++++++++++------------------- src/modules/module-alsa-source.c | 273 +++++++++++++++++++++++---------------- src/modules/module-null-sink.c | 3 +- src/modules/module-pipe-sink.c | 3 +- src/modules/module-pipe-source.c | 3 +- src/pulsecore/asyncmsgq.c | 36 ++++-- src/pulsecore/asyncmsgq.h | 15 ++- src/pulsecore/asyncq.c | 124 +++++++++++++++++- src/pulsecore/asyncq.h | 21 ++- src/pulsecore/rtpoll.c | 55 ++++++-- src/pulsecore/rtpoll.h | 3 +- src/pulsecore/thread-mq.c | 35 +++-- src/pulsecore/thread-mq.h | 5 +- 13 files changed, 524 insertions(+), 300 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 665dbdee..bc95e6c1 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -131,7 +131,6 @@ struct userdata { int64_t frame_index; snd_pcm_sframes_t hwbuf_unused_frames; - snd_pcm_sframes_t avail_min_frames; }; static void fix_tsched_watermark(struct userdata *u) { @@ -140,8 +139,61 @@ static void fix_tsched_watermark(struct userdata *u) { max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size; - if (u->tsched_watermark >= max_use/2) - u->tsched_watermark = max_use/2; + if (u->tsched_watermark >= max_use-u->frame_size) + u->tsched_watermark = max_use-u->frame_size; +} + +static int try_recover(struct userdata *u, const char *call, int err) { + pa_assert(u); + pa_assert(call); + pa_assert(err < 0); + + pa_log_debug("%s: %s", call, snd_strerror(err)); + + if (err == -EAGAIN) { + pa_log_debug("%s: EAGAIN", call); + return 1; + } + + if (err == -EPIPE) + pa_log_debug("%s: Buffer underrun!", call); + + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { + u->first = TRUE; + return 0; + } + + pa_log("%s: %s", call, snd_strerror(err)); + return -1; +} + +static void check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) { + size_t left_to_play; + + if (u->first) + return; + + if (n*u->frame_size < u->hwbuf_size) + left_to_play = u->hwbuf_size - (n*u->frame_size); + else + left_to_play = 0; + + if (left_to_play > 0) + pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + else { + pa_log_info("Underrun!"); + + if (u->use_tsched) { + size_t old_watermark = u->tsched_watermark; + + u->tsched_watermark *= 2; + fix_tsched_watermark(u); + + if (old_watermark != u->tsched_watermark) + pa_log_notice("Increasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); + } + } } static int mmap_write(struct userdata *u) { @@ -154,10 +206,9 @@ static int mmap_write(struct userdata *u) { pa_memchunk chunk; void *p; snd_pcm_sframes_t n; - int err; + int err, r; const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset, frames; - size_t left_to_play; snd_pcm_hwsync(u->pcm_handle); @@ -166,70 +217,40 @@ static int mmap_write(struct userdata *u) { if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { - pa_log_debug("snd_pcm_avail_update: %s", snd_strerror(n)); - - if (err == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; - } - - if (n == -EPIPE) - pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); - - if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) { - u->first = TRUE; + if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) continue; - } + else if (r > 0) + return work_done; - pa_log("snd_pcm_recover: %s", snd_strerror(err)); - return -1; + return r; } + check_left_to_play(u, n); + /* We only use part of the buffer that matches our * dynamically requested latency */ if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) return work_done; - if (n*u->frame_size < u->hwbuf_size) - left_to_play = u->hwbuf_size - (n*u->frame_size); - else - left_to_play = 0; - - pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); - - if (left_to_play <= 0 && !u->first) { - u->tsched_watermark *= 2; - fix_tsched_watermark(u); - pa_log_notice("Underrun! Increasing wakeup watermark to %0.2f ms", - (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); - } - frames = n = n - u->hwbuf_unused_frames; - pa_log_debug("%llu frames to write", (unsigned long long) frames); + pa_log_debug("%lu frames to write", (unsigned long) frames); if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { - pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err)); - - if (err == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; - } - - if (err == -EPIPE) - pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); - - if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { - u->first = TRUE; + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) continue; - } + else if (r > 0) + return work_done; - pa_log("Failed to write data to DSP: %s", snd_strerror(err)); - return -1; + return r; } + /* Make sure that if these memblocks need to be copied they will fit into one slot */ + if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size) + frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -240,7 +261,7 @@ static int mmap_write(struct userdata *u) { p = (uint8_t*) areas[0].addr + (offset * u->frame_size); - chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, 1); + chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE); chunk.length = pa_memblock_get_length(chunk.memblock); chunk.index = 0; @@ -252,30 +273,19 @@ static int mmap_write(struct userdata *u) { if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { - pa_log_debug("snd_pcm_mmap_commit: %s", snd_strerror(err)); - - if (err == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; - } - - if (err == -EPIPE) - pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); - - if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { - u->first = TRUE; + if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) continue; - } + else if (r > 0) + return work_done; - pa_log("Failed to write data to DSP: %s", snd_strerror(err)); - return -1; + return r; } work_done = 1; u->frame_index += frames; - pa_log_debug("wrote %llu frames", (unsigned long long) frames); + pa_log_debug("wrote %lu frames", (unsigned long) frames); if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) return work_done; @@ -283,40 +293,37 @@ static int mmap_write(struct userdata *u) { } static int unix_write(struct userdata *u) { - snd_pcm_status_t *status; int work_done = 0; - snd_pcm_status_alloca(&status); - pa_assert(u); pa_sink_assert_ref(u->sink); for (;;) { void *p; snd_pcm_sframes_t n, frames; - int err; + int r; snd_pcm_hwsync(u->pcm_handle); - snd_pcm_avail_update(u->pcm_handle); - if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { - pa_log("Failed to query DSP status data: %s", snd_strerror(err)); - return -1; - } + if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { - if (PA_UNLIKELY(snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size)) - pa_log_debug("Buffer underrun!"); + if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) + continue; + else if (r > 0) + return work_done; - n = snd_pcm_status_get_avail(status); + return r; + } - /* We only use part of the buffer that matches our - * dynamically requested latency */ + check_left_to_play(u, n); if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) return work_done; n -= u->hwbuf_unused_frames; + pa_log_debug("%lu frames to write", (unsigned long) frames); + if (u->memchunk.length <= 0) pa_sink_render(u->sink, n * u->frame_size, &u->memchunk); @@ -335,21 +342,12 @@ static int unix_write(struct userdata *u) { if (PA_UNLIKELY(frames < 0)) { - if (frames == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; - } - - if (frames == -EPIPE) - pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); - - if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) { - u->first = TRUE; + if ((r = try_recover(u, "snd_pcm_writei", n)) == 0) continue; - } + else if (r > 0) + return work_done; - pa_log("Failed to write data to DSP: %s", snd_strerror(frames)); - return -1; + return r; } u->memchunk.index += frames * u->frame_size; @@ -364,6 +362,8 @@ static int unix_write(struct userdata *u) { u->frame_index += frames; + pa_log_debug("wrote %lu frames", (unsigned long) frames); + if (PA_LIKELY(frames >= n)) return work_done; } @@ -399,8 +399,8 @@ static void update_smoother(struct userdata *u) { return; } - frames = u->frame_index - delay; + /* pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); */ /* snd_pcm_status_get_tstamp(status, ×tamp); */ @@ -422,7 +422,7 @@ static pa_usec_t sink_get_latency(struct userdata *u) { now1 = pa_rtclock_usec(); now2 = pa_smoother_get(u->smoother, now1); - delay = (int64_t) pa_bytes_to_usec(u->frame_index * u->frame_size, &u->sink->sample_spec) - now2; + delay = (int64_t) pa_bytes_to_usec(u->frame_index * u->frame_size, &u->sink->sample_spec) - (int64_t) now2; if (delay > 0) r = (pa_usec_t) delay; @@ -434,28 +434,14 @@ static pa_usec_t sink_get_latency(struct userdata *u) { } static int build_pollfd(struct userdata *u) { - int err; - struct pollfd *pollfd; - int n; - pa_assert(u); pa_assert(u->pcm_handle); - if ((n = snd_pcm_poll_descriptors_count(u->pcm_handle)) < 0) { - pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n)); - return -1; - } - if (u->alsa_rtpoll_item) pa_rtpoll_item_free(u->alsa_rtpoll_item); - u->alsa_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, n); - pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, NULL); - - if ((err = snd_pcm_poll_descriptors(u->pcm_handle, pollfd, n)) < 0) { - pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err)); + if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll))) return -1; - } return 0; } @@ -491,7 +477,7 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { if (usec == (pa_usec_t) -1) usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec); -/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ + pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec); @@ -505,25 +491,27 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { usec /= 2; } -/* pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ + pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); return usec; } static int update_sw_params(struct userdata *u) { + snd_pcm_uframes_t avail_min; int err; - pa_usec_t latency; pa_assert(u); /* Use the full buffer if noone asked us for anything specific */ u->hwbuf_unused_frames = 0; - if (u->use_tsched) + if (u->use_tsched) { + pa_usec_t latency; + if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) != (pa_usec_t) -1) { size_t b; - pa_log_debug("latency set to %llu", (unsigned long long) latency); + pa_log_debug("latency set to %0.2f", (double) latency / PA_USEC_PER_MSEC); b = pa_usec_to_bytes(latency, &u->sink->sample_spec); @@ -538,23 +526,23 @@ static int update_sw_params(struct userdata *u) { fix_tsched_watermark(u); } + } pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames); /* We need at last one frame in the used part of the buffer */ - u->avail_min_frames = u->hwbuf_unused_frames + 1; + avail_min = u->hwbuf_unused_frames + 1; if (u->use_tsched) { pa_usec_t usec; usec = hw_sleep_time(u); - - u->avail_min_frames += (pa_usec_to_bytes(usec, &u->sink->sample_spec) / u->frame_size); + avail_min += pa_usec_to_bytes(usec, &u->sink->sample_spec); } - pa_log_debug("setting avail_min=%lu", (unsigned long) u->avail_min_frames); + pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min); - if ((err = pa_alsa_set_sw_params(u->pcm_handle, u->avail_min_frames)) < 0) { + if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { pa_log("Failed to set software parameters: %s", snd_strerror(err)); return err; } @@ -678,14 +666,6 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse } break; - -/* case PA_SINK_MESSAGE_ADD_INPUT: */ -/* case PA_SINK_MESSAGE_REMOVE_INPUT: */ -/* case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: { */ -/* int r = pa_sink_process_msg(o, code, data, offset, chunk); */ -/* update_hwbuf_unused_frames(u); */ -/* return r; */ -/* } */ } return pa_sink_process_msg(o, code, data, offset, chunk); @@ -1091,10 +1071,9 @@ int pa__init(pa_module*m) { u->use_mmap = use_mmap; u->use_tsched = use_tsched; u->first = TRUE; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->alsa_rtpoll_item = NULL; - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE); usec = pa_rtclock_usec(); @@ -1238,7 +1217,6 @@ int pa__init(pa_module*m) { u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; u->hwbuf_unused_frames = 0; - u->avail_min_frames = 0; u->tsched_watermark = tsched_watermark; u->frame_index = 0; u->hw_dB_supported = FALSE; @@ -1246,12 +1224,10 @@ int pa__init(pa_module*m) { u->hw_volume_min = u->hw_volume_max = 0; if (use_tsched) - if (u->tsched_watermark >= u->hwbuf_size/2) - u->tsched_watermark = pa_frame_align(u->hwbuf_size/2, &ss); + fix_tsched_watermark(u); u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0; u->sink->max_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); - if (!use_tsched) u->sink->min_latency = u->sink->max_latency; @@ -1325,7 +1301,7 @@ int pa__init(pa_module*m) { u->sink->get_volume = sink_get_volume_cb; u->sink->set_volume = sink_set_volume_cb; u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0); - pa_log_info("Using hardware volume control. %s dB scale.", u->hw_dB_supported ? "Using" : "Not using"); + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); } else if (mixer_reset) { pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index bcb3c2d0..3999ada7 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -127,8 +127,70 @@ struct userdata { pa_smoother *smoother; int64_t frame_index; + + snd_pcm_sframes_t hwbuf_unused_frames; }; +static void fix_tsched_watermark(struct userdata *u) { + size_t max_use; + pa_assert(u); + + max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size; + + if (u->tsched_watermark >= max_use-u->frame_size) + u->tsched_watermark = max_use-u->frame_size; +} + +static int try_recover(struct userdata *u, const char *call, int err) { + pa_assert(u); + pa_assert(call); + pa_assert(err < 0); + + pa_log_debug("%s: %s", call, snd_strerror(err)); + + if (err == -EAGAIN) { + pa_log_debug("%s: EAGAIN", call); + return 1; + } + + if (err == -EPIPE) + pa_log_debug("%s: Buffer overrun!", call); + + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { + snd_pcm_start(u->pcm_handle); + return 0; + } + + pa_log("%s: %s", call, snd_strerror(err)); + return -1; +} + +static void check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { + size_t left_to_record; + + if (n*u->frame_size < u->hwbuf_size) + left_to_record = u->hwbuf_size - (n*u->frame_size); + else + left_to_record = 0; + + if (left_to_record > 0) + pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); + else { + pa_log_info("Overrun!"); + + if (u->use_tsched) { + size_t old_watermark = u->tsched_watermark; + + u->tsched_watermark *= 2; + fix_tsched_watermark(u); + + if (old_watermark != u->tsched_watermark) + pa_log_notice("Increasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); + } + } +} + static int mmap_read(struct userdata *u) { int work_done = 0; @@ -137,7 +199,7 @@ static int mmap_read(struct userdata *u) { for (;;) { snd_pcm_sframes_t n; - int err; + int err, r; const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset, frames; pa_memchunk chunk; @@ -147,51 +209,37 @@ static int mmap_read(struct userdata *u) { if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { - pa_log_debug("snd_pcm_avail_update: %s", snd_strerror(n)); - - if (err == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; - } - - if (n == -EPIPE) - pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); - - if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) { - snd_pcm_start(u->pcm_handle); + if ((r = try_recover(u, "snd_pcm_avail_update", err)) == 0) continue; - } + else if (r > 0) + return work_done; - pa_log("snd_pcm_recover: %s", snd_strerror(err)); - return -1; + return r; } + check_left_to_record(u, n); + if (PA_UNLIKELY(n <= 0)) return work_done; frames = n; - if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { + pa_log_debug("%lu frames to read", (unsigned long) frames); - pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err)); - - if (err == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; - } - - if (err == -EPIPE) - pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); + if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { - if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { - snd_pcm_start(u->pcm_handle); + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) continue; - } + else if (r > 0) + return work_done; - pa_log("Failed to write data to DSP: %s", snd_strerror(err)); - return -1; + return r; } + /* Make sure that if these memblocks need to be copied they will fit into one slot */ + if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size) + frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -202,42 +250,28 @@ static int mmap_read(struct userdata *u) { p = (uint8_t*) areas[0].addr + (offset * u->frame_size); - chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, 1); + chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE); chunk.length = pa_memblock_get_length(chunk.memblock); chunk.index = 0; pa_source_post(u->source, &chunk); - - /* FIXME: Maybe we can do something to keep this memory block - * a little bit longer around? */ pa_memblock_unref_fixed(chunk.memblock); if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { - pa_log_debug("snd_pcm_mmap_commit: %s", snd_strerror(err)); - - if (err == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; - } - - if (err == -EPIPE) - pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); - - if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { - snd_pcm_start(u->pcm_handle); + if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) continue; - } + else if (r > 0) + return work_done; - pa_log("Failed to write data to DSP: %s", snd_strerror(err)); - return -1; + return r; } work_done = 1; u->frame_index += frames; - pa_log_debug("read %llu frames", (unsigned long long) frames); + pa_log_debug("read %lu frames", (unsigned long) frames); if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) return work_done; @@ -245,31 +279,30 @@ static int mmap_read(struct userdata *u) { } static int unix_read(struct userdata *u) { - snd_pcm_status_t *status; int work_done = 0; - snd_pcm_status_alloca(&status); - pa_assert(u); pa_source_assert_ref(u->source); for (;;) { void *p; snd_pcm_sframes_t n, frames; - int err; + int r; pa_memchunk chunk; snd_pcm_hwsync(u->pcm_handle); - if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { - pa_log("Failed to query DSP status data: %s", snd_strerror(err)); - return -1; - } + if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { + + if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) + continue; + else if (r > 0) + return work_done; - if (PA_UNLIKELY(snd_pcm_status_get_avail_max(status)*u->frame_size >= u->hwbuf_size)) - pa_log_debug("Buffer overrun!"); + return r; + } - n = snd_pcm_status_get_avail(status); + check_left_to_record(u, n); if (PA_UNLIKELY(n <= 0)) return work_done; @@ -281,6 +314,8 @@ static int unix_read(struct userdata *u) { if (frames > n) frames = n; + pa_log_debug("%lu frames to read", (unsigned long) n); + p = pa_memblock_acquire(chunk.memblock); frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames); pa_memblock_release(chunk.memblock); @@ -290,20 +325,12 @@ static int unix_read(struct userdata *u) { if (PA_UNLIKELY(frames < 0)) { pa_memblock_unref(chunk.memblock); - if (frames == -EAGAIN) { - pa_log_debug("EAGAIN"); - return work_done; - } - - if (frames == -EPIPE) - pa_log_debug("snd_pcm_avail_update: Buffer overrun!"); - - if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) - snd_pcm_start(u->pcm_handle); + if ((r = try_recover(u, "snd_pcm_readi", n)) == 0) continue; + else if (r > 0) + return work_done; - pa_log("Failed to read data from DSP: %s", snd_strerror(frames)); - return -1; + return r; } chunk.index = 0; @@ -316,6 +343,8 @@ static int unix_read(struct userdata *u) { u->frame_index += frames; + pa_log_debug("read %lu frames", (unsigned long) frames); + if (PA_LIKELY(frames >= n)) return work_done; } @@ -344,46 +373,37 @@ static void update_smoother(struct userdata *u) { now1 = pa_rtclock_usec(); now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec); + pa_smoother_put(u->smoother, now1, now2); } static pa_usec_t source_get_latency(struct userdata *u) { pa_usec_t r = 0; int64_t delay; + pa_usec_t now1, now2; pa_assert(u); - delay = pa_smoother_get(u->smoother, pa_rtclock_usec()) - u->frame_index; + now1 = pa_rtclock_usec(); + now2 = pa_smoother_get(u->smoother, now1); + + delay = (int64_t) now2 - pa_bytes_to_usec(u->frame_index * u->frame_size, &u->source->sample_spec); if (delay > 0) - r = pa_bytes_to_usec(delay * u->frame_size, &u->source->sample_spec); + r = (pa_usec_t) delay; return r; } static int build_pollfd(struct userdata *u) { - int err; - struct pollfd *pollfd; - int n; - pa_assert(u); pa_assert(u->pcm_handle); - if ((n = snd_pcm_poll_descriptors_count(u->pcm_handle)) < 0) { - pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n)); - return -1; - } - if (u->alsa_rtpoll_item) pa_rtpoll_item_free(u->alsa_rtpoll_item); - u->alsa_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, n); - pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, NULL); - - if ((err = snd_pcm_poll_descriptors(u->pcm_handle, pollfd, n)) < 0) { - pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err)); + if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll))) return -1; - } return 0; } @@ -418,7 +438,7 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { if (usec == (pa_usec_t) -1) usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); - pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); +/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec); @@ -427,29 +447,55 @@ static pa_usec_t hw_sleep_time(struct userdata *u) { else usec /= 2; - pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); +/* pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ return usec; } static int update_sw_params(struct userdata *u) { - size_t avail_min; + snd_pcm_uframes_t avail_min; int err; pa_assert(u); + /* Use the full buffer if noone asked us for anything specific */ + u->hwbuf_unused_frames = 0; + if (u->use_tsched) { - pa_usec_t usec; + pa_usec_t latency; - usec = hw_sleep_time(u); + if ((latency = pa_source_get_requested_latency_within_thread(u->source)) != (pa_usec_t) -1) { + size_t b; - avail_min = pa_usec_to_bytes(usec, &u->source->sample_spec); + pa_log_debug("latency set to %0.2f", (double) latency / PA_USEC_PER_MSEC); - if (avail_min <= 0) - avail_min = 1; + b = pa_usec_to_bytes(latency, &u->source->sample_spec); - } else - avail_min = 1; + /* We need at least one sample in our buffer */ + + if (PA_UNLIKELY(b < u->frame_size)) + b = u->frame_size; + + u->hwbuf_unused_frames = + PA_LIKELY(b < u->hwbuf_size) ? + ((u->hwbuf_size - b) / u->frame_size) : 0; + + fix_tsched_watermark(u); + } + } + + pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames); + + avail_min = 1; + + if (u->use_tsched) { + pa_usec_t usec; + + usec = hw_sleep_time(u); + avail_min += pa_usec_to_bytes(usec, &u->source->sample_spec); + } + + pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min); if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) { pa_log("Failed to set software parameters: %s", snd_strerror(err)); @@ -649,7 +695,6 @@ static int source_set_volume_cb(pa_source *s) { long alsa_vol; pa_volume_t vol; - pa_assert(snd_mixer_selem_has_capture_channel(u->mixer_elem, u->mixer_map[i])); vol = PA_MIN(s->volume.values[i], PA_VOLUME_NORM); @@ -670,7 +715,6 @@ static int source_set_volume_cb(pa_source *s) { u->hw_dB_supported = FALSE; } - alsa_vol = (long) roundf(((float) vol * (u->hw_volume_max - u->hw_volume_min)) / PA_VOLUME_NORM) + u->hw_volume_min; alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max); @@ -745,6 +789,8 @@ static void thread_func(void *userdata) { for (;;) { int ret; + pa_log_debug("loop"); + /* Read some data and pass it to the sources */ if (PA_SOURCE_OPENED(u->source->thread_info.state)) { int work_done = 0; @@ -757,6 +803,8 @@ static void thread_func(void *userdata) { if (work_done < 0) goto fail; + pa_log_debug("work_done = %i", work_done); + if (work_done) update_smoother(u); @@ -806,8 +854,7 @@ static void thread_func(void *userdata) { } if (revents & (POLLERR|POLLNVAL|POLLHUP)) { - - if (pa_alsa_recover_from_poll(u->pcm_handle, revents)) + if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0) goto fail; snd_pcm_start(u->pcm_handle); @@ -910,10 +957,9 @@ int pa__init(pa_module*m) { m->userdata = u; u->use_mmap = use_mmap; u->use_tsched = use_tsched; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->alsa_rtpoll_item = NULL; - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE); pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); @@ -951,7 +997,7 @@ int pa__init(pa_module*m) { if (use_mmap && !b) { pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode."); - u->use_mmap = use_mmap = b; + u->use_mmap = use_mmap = FALSE; } if (use_tsched && (!b || !d)) { @@ -1030,7 +1076,7 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap_rewrite" : (u->use_mmap ? "mmap" : "serial")); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); pa_source_new_data_done(&data); @@ -1052,14 +1098,17 @@ int pa__init(pa_module*m) { u->fragment_size = frag_size = period_frames * frame_size; u->nfragments = nfrags; u->hwbuf_size = u->fragment_size * nfrags; + u->hwbuf_unused_frames = 0; u->tsched_watermark = tsched_watermark; u->frame_index = 0; u->hw_dB_supported = FALSE; u->hw_dB_min = u->hw_dB_max = 0; u->hw_volume_min = u->hw_volume_max = 0; - u->source->max_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + if (use_tsched) + fix_tsched_watermark(u); + u->source->max_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); if (!use_tsched) u->source->min_latency = u->source->max_latency; @@ -1095,7 +1144,7 @@ int pa__init(pa_module*m) { pa_log_info("Device has less than 4 volume levels. Falling back to software volume control."); suitable = FALSE; - } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { + } else if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); @@ -1122,7 +1171,7 @@ int pa__init(pa_module*m) { u->source->get_volume = source_get_volume_cb; u->source->set_volume = source_set_volume_cb; u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0); - pa_log_info("Using hardware volume control. %s dB scale.", u->hw_dB_supported ? "Using" : "Not using"); + pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported"); } else if (mixer_reset) { pa_log_info("Using software volume control. Trying to reset sound card to 0 dB."); diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 6e59c62c..2301f088 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -188,9 +188,8 @@ int pa__init(pa_module*m) { u->core = m->core; u->module = m; m->userdata = u; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); pa_sink_new_data_init(&data); data.driver = __FILE__; diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index 73a52112..870b32ff 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -227,9 +227,8 @@ int pa__init(pa_module*m) { u->module = m; m->userdata = u; pa_memchunk_reset(&u->memchunk); - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME)); diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index cf88c823..5bf4da1f 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -204,9 +204,8 @@ int pa__init(pa_module*m) { u->module = m; m->userdata = u; pa_memchunk_reset(&u->memchunk); - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME)); diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c index 96b43a71..eba1c2cb 100644 --- a/src/pulsecore/asyncmsgq.c +++ b/src/pulsecore/asyncmsgq.c @@ -136,7 +136,7 @@ void pa_asyncmsgq_post(pa_asyncmsgq *a, pa_msgobject *object, int code, const vo /* This mutex makes the queue multiple-writer safe. This lock is only used on the writing side */ pa_mutex_lock(a->mutex); - pa_assert_se(pa_asyncq_push(a->asyncq, i, 1) == 0); + pa_asyncq_post(a->asyncq, i); pa_mutex_unlock(a->mutex); } @@ -163,7 +163,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi /* Thus mutex makes the queue multiple-writer safe. This lock is only used on the writing side */ pa_mutex_lock(a->mutex); - pa_assert_se(pa_asyncq_push(a->asyncq, &i, 1) == 0); + pa_assert_se(pa_asyncq_push(a->asyncq, &i, TRUE) == 0); pa_mutex_unlock(a->mutex); pa_semaphore_wait(i.semaphore); @@ -174,7 +174,7 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi return i.ret; } -int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, int wait) { +int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait) { pa_assert(PA_REFCNT_VALUE(a) > 0); pa_assert(!a->current); @@ -276,22 +276,40 @@ int pa_asyncmsgq_process_one(pa_asyncmsgq *a) { return 1; } -int pa_asyncmsgq_get_fd(pa_asyncmsgq *a) { +int pa_asyncmsgq_read_fd(pa_asyncmsgq *a) { pa_assert(PA_REFCNT_VALUE(a) > 0); - return pa_asyncq_get_fd(a->asyncq); + return pa_asyncq_read_fd(a->asyncq); } -int pa_asyncmsgq_before_poll(pa_asyncmsgq *a) { +int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a) { pa_assert(PA_REFCNT_VALUE(a) > 0); - return pa_asyncq_before_poll(a->asyncq); + return pa_asyncq_read_before_poll(a->asyncq); } -void pa_asyncmsgq_after_poll(pa_asyncmsgq *a) { +void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a) { pa_assert(PA_REFCNT_VALUE(a) > 0); - pa_asyncq_after_poll(a->asyncq); + pa_asyncq_read_after_poll(a->asyncq); +} + +int pa_asyncmsgq_write_fd(pa_asyncmsgq *a) { + pa_assert(PA_REFCNT_VALUE(a) > 0); + + return pa_asyncq_write_fd(a->asyncq); +} + +void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a) { + pa_assert(PA_REFCNT_VALUE(a) > 0); + + pa_asyncq_write_before_poll(a->asyncq); +} + +void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a) { + pa_assert(PA_REFCNT_VALUE(a) > 0); + + pa_asyncq_write_after_poll(a->asyncq); } int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk) { diff --git a/src/pulsecore/asyncmsgq.h b/src/pulsecore/asyncmsgq.h index 575f760f..93f1ce86 100644 --- a/src/pulsecore/asyncmsgq.h +++ b/src/pulsecore/asyncmsgq.h @@ -62,15 +62,20 @@ void pa_asyncmsgq_unref(pa_asyncmsgq* q); void pa_asyncmsgq_post(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk, pa_free_cb_t userdata_free_cb); int pa_asyncmsgq_send(pa_asyncmsgq *q, pa_msgobject *object, int code, const void *userdata, int64_t offset, const pa_memchunk *memchunk); -int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, int wait); +int pa_asyncmsgq_get(pa_asyncmsgq *q, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *memchunk, pa_bool_t wait); int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *memchunk); void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret); int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code); int pa_asyncmsgq_process_one(pa_asyncmsgq *a); -/* Just for the reading side */ -int pa_asyncmsgq_get_fd(pa_asyncmsgq *q); -int pa_asyncmsgq_before_poll(pa_asyncmsgq *a); -void pa_asyncmsgq_after_poll(pa_asyncmsgq *a); +/* For the reading side */ +int pa_asyncmsgq_read_fd(pa_asyncmsgq *q); +int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a); +void pa_asyncmsgq_read_after_poll(pa_asyncmsgq *a); + +/* For the write side */ +int pa_asyncmsgq_write_fd(pa_asyncmsgq *q); +void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a); +void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a); #endif diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c index 75b15c0e..34506e49 100644 --- a/src/pulsecore/asyncq.c +++ b/src/pulsecore/asyncq.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include "asyncq.h" @@ -51,13 +53,24 @@ #define _Y do { } while(0) #endif +struct localq { + void *data; + PA_LLIST_FIELDS(struct localq); +}; + struct pa_asyncq { unsigned size; unsigned read_idx; unsigned write_idx; pa_fdsem *read_fdsem, *write_fdsem; + + PA_LLIST_HEAD(struct localq, localq); + struct localq *last_localq; + pa_bool_t waiting_for_post; }; +PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree); + #define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq)))) static int is_power_of_two(unsigned size) { @@ -80,6 +93,10 @@ pa_asyncq *pa_asyncq_new(unsigned size) { l->size = size; + PA_LLIST_HEAD_INIT(struct localq, l->localq); + l->last_localq = NULL; + l->waiting_for_post = FALSE; + if (!(l->read_fdsem = pa_fdsem_new())) { pa_xfree(l); return NULL; @@ -95,6 +112,7 @@ pa_asyncq *pa_asyncq_new(unsigned size) { } void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) { + struct localq *q; pa_assert(l); if (free_cb) { @@ -104,12 +122,22 @@ void pa_asyncq_free(pa_asyncq *l, pa_free_cb_t free_cb) { free_cb(p); } + while ((q = l->localq)) { + if (free_cb) + free_cb(q->data); + + PA_LLIST_REMOVE(struct localq, l->localq, q); + + if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0) + pa_xfree(q); + } + pa_fdsem_free(l->read_fdsem); pa_fdsem_free(l->write_fdsem); pa_xfree(l); } -int pa_asyncq_push(pa_asyncq*l, void *p, int wait) { +static int push(pa_asyncq*l, void *p, pa_bool_t wait) { int idx; pa_atomic_ptr_t *cells; @@ -141,7 +169,63 @@ int pa_asyncq_push(pa_asyncq*l, void *p, int wait) { return 0; } -void* pa_asyncq_pop(pa_asyncq*l, int wait) { +static pa_bool_t flush_postq(pa_asyncq *l) { + struct localq *q; + + pa_assert(l); + + while ((q = l->last_localq)) { + + if (push(l, q->data, FALSE) < 0) + return FALSE; + + l->last_localq = q->prev; + + PA_LLIST_REMOVE(struct localq, l->localq, q); + + if (pa_flist_push(PA_STATIC_FLIST_GET(localq), q) < 0) + pa_xfree(q); + } + + return TRUE; +} + +int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) { + pa_assert(l); + + if (!flush_postq(l)) + return -1; + + return push(l, p, wait); +} + +void pa_asyncq_post(pa_asyncq*l, void *p) { + struct localq *q; + + pa_assert(l); + pa_assert(p); + + if (pa_asyncq_push(l, p, FALSE) >= 0) + return; + + /* OK, we couldn't push anything in the queue. So let's queue it + * locally and push it later */ + + pa_log("q overrun, queuing locally"); + + if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq)))) + q = pa_xnew(struct localq, 1); + + q->data = p; + PA_LLIST_PREPEND(struct localq, l->localq, q); + + if (!l->last_localq) + l->last_localq = q; + + return; +} + +void* pa_asyncq_pop(pa_asyncq*l, pa_bool_t wait) { int idx; void *ret; pa_atomic_ptr_t *cells; @@ -178,13 +262,13 @@ void* pa_asyncq_pop(pa_asyncq*l, int wait) { return ret; } -int pa_asyncq_get_fd(pa_asyncq *q) { +int pa_asyncq_read_fd(pa_asyncq *q) { pa_assert(q); return pa_fdsem_get(q->write_fdsem); } -int pa_asyncq_before_poll(pa_asyncq *l) { +int pa_asyncq_read_before_poll(pa_asyncq *l) { int idx; pa_atomic_ptr_t *cells; @@ -206,8 +290,38 @@ int pa_asyncq_before_poll(pa_asyncq *l) { return 0; } -void pa_asyncq_after_poll(pa_asyncq *l) { +void pa_asyncq_read_after_poll(pa_asyncq *l) { pa_assert(l); pa_fdsem_after_poll(l->write_fdsem); } + +int pa_asyncq_write_fd(pa_asyncq *q) { + pa_assert(q); + + return pa_fdsem_get(q->read_fdsem); +} + +void pa_asyncq_write_before_poll(pa_asyncq *l) { + pa_assert(l); + + for (;;) { + + if (flush_postq(l)) + break; + + if (pa_fdsem_before_poll(l->read_fdsem) >= 0) { + l->waiting_for_post = TRUE; + break; + } + } +} + +void pa_asyncq_write_after_poll(pa_asyncq *l) { + pa_assert(l); + + if (l->waiting_for_post) { + pa_fdsem_after_poll(l->read_fdsem); + l->waiting_for_post = FALSE; + } +} diff --git a/src/pulsecore/asyncq.h b/src/pulsecore/asyncq.h index 53d45866..4cdf8cd0 100644 --- a/src/pulsecore/asyncq.h +++ b/src/pulsecore/asyncq.h @@ -26,6 +26,7 @@ #include #include +#include /* A simple, asynchronous, lock-free (if requested also wait-free) * queue. Not multiple-reader/multiple-writer safe. If that is @@ -46,11 +47,21 @@ typedef struct pa_asyncq pa_asyncq; pa_asyncq* pa_asyncq_new(unsigned size); void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb); -void* pa_asyncq_pop(pa_asyncq *q, int wait); -int pa_asyncq_push(pa_asyncq *q, void *p, int wait); +void* pa_asyncq_pop(pa_asyncq *q, pa_bool_t wait); +int pa_asyncq_push(pa_asyncq *q, void *p, pa_bool_t wait); -int pa_asyncq_get_fd(pa_asyncq *q); -int pa_asyncq_before_poll(pa_asyncq *a); -void pa_asyncq_after_poll(pa_asyncq *a); +/* Similar to pa_asyncq_push(), but if the queue is full, postpone it + * locally and delay until pa_asyncq_before_poll_post() */ +void pa_asyncq_post(pa_asyncq*l, void *p); + +/* For the reading side */ +int pa_asyncq_read_fd(pa_asyncq *q); +int pa_asyncq_read_before_poll(pa_asyncq *a); +void pa_asyncq_read_after_poll(pa_asyncq *a); + +/* For the writing side */ +int pa_asyncq_write_fd(pa_asyncq *q); +void pa_asyncq_write_before_poll(pa_asyncq *a); +void pa_asyncq_write_after_poll(pa_asyncq *a); #endif diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index 734f344f..c3e76cac 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -661,23 +661,23 @@ pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio return i; } -static int asyncmsgq_before(pa_rtpoll_item *i) { +static int asyncmsgq_read_before(pa_rtpoll_item *i) { pa_assert(i); - if (pa_asyncmsgq_before_poll(i->userdata) < 0) + if (pa_asyncmsgq_read_before_poll(i->userdata) < 0) return 1; /* 1 means immediate restart of the loop */ return 0; } -static void asyncmsgq_after(pa_rtpoll_item *i) { +static void asyncmsgq_read_after(pa_rtpoll_item *i) { pa_assert(i); pa_assert((i->pollfd[0].revents & ~POLLIN) == 0); - pa_asyncmsgq_after_poll(i->userdata); + pa_asyncmsgq_read_after_poll(i->userdata); } -static int asyncmsgq_work(pa_rtpoll_item *i) { +static int asyncmsgq_read_work(pa_rtpoll_item *i) { pa_msgobject *object; int code; void *data; @@ -703,7 +703,7 @@ static int asyncmsgq_work(pa_rtpoll_item *i) { return 0; } -pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) { +pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) { pa_rtpoll_item *i; struct pollfd *pollfd; @@ -713,12 +713,47 @@ pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t i = pa_rtpoll_item_new(p, prio, 1); pollfd = pa_rtpoll_item_get_pollfd(i, NULL); - pollfd->fd = pa_asyncmsgq_get_fd(q); + pollfd->fd = pa_asyncmsgq_read_fd(q); pollfd->events = POLLIN; - i->before_cb = asyncmsgq_before; - i->after_cb = asyncmsgq_after; - i->work_cb = asyncmsgq_work; + i->before_cb = asyncmsgq_read_before; + i->after_cb = asyncmsgq_read_after; + i->work_cb = asyncmsgq_read_work; + i->userdata = q; + + return i; +} + +static int asyncmsgq_write_before(pa_rtpoll_item *i) { + pa_assert(i); + + pa_asyncmsgq_write_before_poll(i->userdata); + return 0; +} + +static void asyncmsgq_write_after(pa_rtpoll_item *i) { + pa_assert(i); + + pa_assert((i->pollfd[0].revents & ~POLLIN) == 0); + pa_asyncmsgq_write_after_poll(i->userdata); +} + +pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q) { + pa_rtpoll_item *i; + struct pollfd *pollfd; + + pa_assert(p); + pa_assert(q); + + i = pa_rtpoll_item_new(p, prio, 1); + + pollfd = pa_rtpoll_item_get_pollfd(i, NULL); + pollfd->fd = pa_asyncmsgq_write_fd(q); + pollfd->events = POLLIN; + + i->before_cb = asyncmsgq_write_before; + i->after_cb = asyncmsgq_write_after; + i->work_cb = NULL; i->userdata = q; return i; diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h index f7f96e67..6d72eb54 100644 --- a/src/pulsecore/rtpoll.h +++ b/src/pulsecore/rtpoll.h @@ -106,7 +106,8 @@ void pa_rtpoll_item_set_userdata(pa_rtpoll_item *i, void *userdata); void* pa_rtpoll_item_get_userdata(pa_rtpoll_item *i); pa_rtpoll_item *pa_rtpoll_item_new_fdsem(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_fdsem *s); -pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q); +pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_read(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q); +pa_rtpoll_item *pa_rtpoll_item_new_asyncmsgq_write(pa_rtpoll *p, pa_rtpoll_priority_t prio, pa_asyncmsgq *q); /* Requests the loop to exit. Will cause the next iteration of * pa_rtpoll_run() to return 0 */ diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c index 9b879425..7e39c577 100644 --- a/src/pulsecore/thread-mq.c +++ b/src/pulsecore/thread-mq.c @@ -43,15 +43,15 @@ PA_STATIC_TLS_DECLARE_NO_FREE(thread_mq); -static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { +static void asyncmsgq_read_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { pa_thread_mq *q = userdata; pa_asyncmsgq *aq; - pa_assert(pa_asyncmsgq_get_fd(q->outq) == fd); + pa_assert(pa_asyncmsgq_read_fd(q->outq) == fd); pa_assert(events == PA_IO_EVENT_INPUT); pa_asyncmsgq_ref(aq = q->outq); - pa_asyncmsgq_after_poll(aq); + pa_asyncmsgq_write_after_poll(aq); for (;;) { pa_msgobject *object; @@ -68,14 +68,24 @@ static void asyncmsgq_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_even pa_asyncmsgq_done(aq, ret); } - if (pa_asyncmsgq_before_poll(aq) == 0) + if (pa_asyncmsgq_read_before_poll(aq) == 0) break; } pa_asyncmsgq_unref(aq); } -void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop) { +static void asyncmsgq_write_cb(pa_mainloop_api*api, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { + pa_thread_mq *q = userdata; + + pa_assert(pa_asyncmsgq_write_fd(q->inq) == fd); + pa_assert(events == PA_IO_EVENT_INPUT); + + pa_asyncmsgq_write_after_poll(q->inq); + pa_asyncmsgq_write_before_poll(q->inq); +} + +void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll) { pa_assert(q); pa_assert(mainloop); @@ -83,15 +93,22 @@ void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop) { pa_assert_se(q->inq = pa_asyncmsgq_new(0)); pa_assert_se(q->outq = pa_asyncmsgq_new(0)); - pa_assert_se(pa_asyncmsgq_before_poll(q->outq) == 0); - pa_assert_se(q->io_event = mainloop->io_new(mainloop, pa_asyncmsgq_get_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_cb, q)); + pa_assert_se(pa_asyncmsgq_read_before_poll(q->outq) == 0); + pa_assert_se(q->read_event = mainloop->io_new(mainloop, pa_asyncmsgq_read_fd(q->outq), PA_IO_EVENT_INPUT, asyncmsgq_read_cb, q)); + + pa_asyncmsgq_write_before_poll(q->inq); + pa_assert_se(q->write_event = mainloop->io_new(mainloop, pa_asyncmsgq_write_fd(q->inq), PA_IO_EVENT_INPUT, asyncmsgq_write_cb, q)); + + pa_rtpoll_item_new_asyncmsgq_read(rtpoll, PA_RTPOLL_EARLY, q->inq); + pa_rtpoll_item_new_asyncmsgq_write(rtpoll, PA_RTPOLL_LATE, q->outq); } void pa_thread_mq_done(pa_thread_mq *q) { pa_assert(q); - q->mainloop->io_free(q->io_event); - q->io_event = NULL; + q->mainloop->io_free(q->read_event); + q->mainloop->io_free(q->write_event); + q->read_event = q->write_event = NULL; pa_asyncmsgq_unref(q->inq); pa_asyncmsgq_unref(q->outq); diff --git a/src/pulsecore/thread-mq.h b/src/pulsecore/thread-mq.h index 13b6e01f..0ae49f8c 100644 --- a/src/pulsecore/thread-mq.h +++ b/src/pulsecore/thread-mq.h @@ -26,6 +26,7 @@ #include #include +#include /* Two way communication between a thread and a mainloop. Before the * thread is started a pa_pthread_mq should be initialized and than @@ -34,10 +35,10 @@ typedef struct pa_thread_mq { pa_mainloop_api *mainloop; pa_asyncmsgq *inq, *outq; - pa_io_event *io_event; + pa_io_event *read_event, *write_event; } pa_thread_mq; -void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop); +void pa_thread_mq_init(pa_thread_mq *q, pa_mainloop_api *mainloop, pa_rtpoll *rtpoll); void pa_thread_mq_done(pa_thread_mq *q); /* Install the specified pa_thread_mq object for the current thread */ -- cgit From c8fc223fe902e66e99c402a4854bfb4e76c7a0c0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 18:56:19 +0000 Subject: add stripnul to build git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2309 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index 45e3de71..28882085 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -256,7 +256,8 @@ noinst_PROGRAMS = \ remix-test \ envelope-test \ proplist-test \ - rtstutter + rtstutter \ + stripnul if HAVE_SIGXCPU noinst_PROGRAMS += \ @@ -436,6 +437,11 @@ rtstutter_LDADD = $(AM_LDADD) libpulsecore.la rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) +stripnul_SOURCES = tests/stripnul.c +stripnul_LDADD = $(AM_LDADD) libpulsecore.la +stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) +stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS) + ################################### # Client library # ################################### -- cgit From 067a68abf9563e18c837a6b824f096ce9e7f1914 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 19:22:56 +0000 Subject: fix build for auxiliary modules git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2310 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-combine.c | 9 ++++----- src/modules/module-esound-sink.c | 3 +-- src/modules/module-jack-sink.c | 5 ++--- src/modules/module-jack-source.c | 5 ++--- src/modules/module-ladspa-sink.c | 8 ++++---- src/modules/module-oss.c | 3 +-- src/modules/module-remap-sink.c | 10 ++++------ src/modules/module-tunnel.c | 3 +-- 8 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 6f99ae43..e15654fa 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -384,7 +384,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { /* Set up the queue from the sink thread to us */ pa_assert(!o->inq_rtpoll_item); - o->inq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq( + o->inq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read( i->sink->rtpoll, PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */ o->inq); @@ -584,7 +584,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse /* Create pa_asyncmsgq to the sink thread */ - op->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq( + op->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read( u->rtpoll, PA_RTPOLL_EARLY-1, /* This item is very important */ op->outq); @@ -786,7 +786,7 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { /* If the sink is not yet started, we need to do the activation ourselves */ PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, o); - o->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq( + o->outq_rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read( u->rtpoll, PA_RTPOLL_EARLY-1, /* This item is very important */ o->outq); @@ -945,8 +945,8 @@ int pa__init(pa_module*m) { u->master = NULL; u->time_event = NULL; u->adjust_time = DEFAULT_ADJUST_TIME; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->thread = NULL; u->resample_method = resample_method; u->outputs = pa_idxset_new(NULL, NULL); @@ -955,7 +955,6 @@ int pa__init(pa_module*m) { PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs); pa_atomic_store(&u->thread_info.running, FALSE); u->thread_info.in_null_mode = FALSE; - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) { pa_log("Failed to parse adjust_time value"); diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 51c76dcb..9a4ba58e 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -538,9 +538,8 @@ int pa__init(pa_module*m) { pa_memchunk_reset(&u->memchunk); u->offset = 0; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->rtpoll_item = NULL; u->format = diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c index 80a14a6f..f97041a8 100644 --- a/src/modules/module-jack-sink.c +++ b/src/modules/module-jack-sink.c @@ -301,9 +301,8 @@ int pa__init(pa_module*m) { u->module = m; m->userdata = u; u->saved_frame_time_valid = FALSE; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); /* The queue linking the JACK thread and our RT thread */ u->jack_msgq = pa_asyncmsgq_new(0); @@ -313,7 +312,7 @@ int pa__init(pa_module*m) { * all other drivers make: supplying the audio device with data is * the top priority -- and as long as that is possible we don't do * anything else */ - u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); + u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { pa_log("jack_client_open() failed."); diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c index a687563d..18ffa55c 100644 --- a/src/modules/module-jack-source.c +++ b/src/modules/module-jack-source.c @@ -279,12 +279,11 @@ int pa__init(pa_module*m) { m->userdata = u; u->saved_frame_time_valid = FALSE; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->jack_msgq = pa_asyncmsgq_new(0); - u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); + u->rtpoll_item = pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->jack_msgq); if (!(u->client = jack_client_open(client_name, server_name ? JackServerName : JackNullOption, &status, server_name))) { pa_log("jack_client_open() failed."); diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 14f14b14..aa398a28 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -217,7 +217,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } /* Called from I/O thread context */ -static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); @@ -228,7 +228,7 @@ static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { } /* Called from I/O thread context */ -static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); @@ -650,8 +650,8 @@ int pa__init(pa_module*m) { u->sink_input->parent.process_msg = sink_input_process_msg; u->sink_input->pop = sink_input_pop_cb; - u->sink_input->rewind = sink_input_rewind_cb; - u->sink_input->set_max_rewind = sink_input_set_max_rewind_cb; + u->sink_input->process_rewind = sink_input_process_rewind_cb; + u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c index 149c70d5..f07d82a0 100644 --- a/src/modules/module-oss.c +++ b/src/modules/module-oss.c @@ -1234,9 +1234,8 @@ int pa__init(pa_module*m) { u->in_nfrags = u->out_nfrags = u->nfrags = nfrags; u->out_fragment_size = u->in_fragment_size = u->frag_size = frag_size; u->use_mmap = use_mmap; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->rtpoll_item = NULL; build_pollfd(u); diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 3f70ee1d..6a16321d 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -143,8 +143,6 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t return pa_sink_input_process_msg(o, code, data, offset, chunk); } -static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes); - /* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; @@ -159,7 +157,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } /* Called from I/O thread context */ -static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); @@ -170,7 +168,7 @@ static void sink_input_rewind_cb(pa_sink_input *i, size_t nbytes) { } /* Called from I/O thread context */ -static void sink_input_set_max_rewind_cb(pa_sink_input *i, size_t nbytes) { +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; pa_sink_input_assert_ref(i); @@ -321,8 +319,8 @@ int pa__init(pa_module*m) { u->sink_input->parent.process_msg = sink_input_process_msg; u->sink_input->pop = sink_input_pop_cb; - u->sink_input->rewind = sink_input_rewind_cb; - u->sink_input->set_max_rewind = sink_input_set_max_rewind_cb; + u->sink_input->process_rewind = sink_input_process_rewind_cb; + u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 5483be39..74d5a826 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -1329,9 +1329,8 @@ int pa__init(pa_module*m) { u->auth_cookie_in_property = FALSE; u->time_event = NULL; - pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); - pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); + pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); if (load_key(u, pa_modargs_get_value(ma, "cookie", NULL)) < 0) goto fail; -- cgit From 5353cf45f5a35b33076cc440cc8a8ba587418315 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 19:55:04 +0000 Subject: fix size of requested_latency git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2311 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 97491f43..7bc4a706 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -112,7 +112,7 @@ struct pa_sink { pa_bool_t soft_muted; pa_bool_t requested_latency_valid; - size_t requested_latency; + pa_usec_t requested_latency; /* The number of bytes we need keep around to be able to satisfy * every DMA buffer rewrite */ -- cgit From caf742a0764589cd77bf3b28e4b80c761d83e3bb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Apr 2008 23:22:51 +0000 Subject: define minimal and maximal wakeup/sleep times; check for underrun condition only once during buffer fillup git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2312 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 43 ++++++++++++++++++++++++++++++++++------ src/modules/module-alsa-source.c | 35 ++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index bc95e6c1..5cc27f11 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -94,6 +94,8 @@ static const char* const valid_modargs[] = { #define DEFAULT_DEVICE "default" #define DEFAULT_TSCHED_BUFFER_USEC (5*PA_USEC_PER_SEC) /* 5s */ #define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ +#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ struct userdata { pa_core *core; @@ -121,7 +123,7 @@ struct userdata { pa_bool_t use_mmap, use_tsched; - pa_bool_t first; + pa_bool_t first, after_rewind; pa_rtpoll_item *alsa_rtpoll_item; @@ -135,12 +137,29 @@ struct userdata { static void fix_tsched_watermark(struct userdata *u) { size_t max_use; + size_t min_sleep, min_wakeup; pa_assert(u); max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size; - if (u->tsched_watermark >= max_use-u->frame_size) - u->tsched_watermark = max_use-u->frame_size; + min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->sink->sample_spec); + min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->sink->sample_spec); + + if (min_sleep > max_use/2) + min_sleep = pa_frame_align(max_use/2, &u->sink->sample_spec); + if (min_sleep < u->frame_size) + min_sleep = u->frame_size; + + if (min_wakeup > max_use/2) + min_wakeup = pa_frame_align(max_use/2, &u->sink->sample_spec); + if (min_wakeup < u->frame_size) + min_wakeup = u->frame_size; + + if (u->tsched_watermark > max_use-min_sleep) + u->tsched_watermark = max_use-min_sleep; + + if (u->tsched_watermark < min_wakeup) + u->tsched_watermark = min_wakeup; } static int try_recover(struct userdata *u, const char *call, int err) { @@ -170,7 +189,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { static void check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) { size_t left_to_play; - if (u->first) + if (u->first || u->after_rewind) return; if (n*u->frame_size < u->hwbuf_size) @@ -198,6 +217,7 @@ static void check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) { static int mmap_write(struct userdata *u) { int work_done = 0; + pa_bool_t checked_left_to_play = FALSE; pa_assert(u); pa_sink_assert_ref(u->sink); @@ -225,7 +245,10 @@ static int mmap_write(struct userdata *u) { return r; } - check_left_to_play(u, n); + if (!checked_left_to_play) { + check_left_to_play(u, n); + checked_left_to_play = TRUE; + } /* We only use part of the buffer that matches our * dynamically requested latency */ @@ -294,6 +317,7 @@ static int mmap_write(struct userdata *u) { static int unix_write(struct userdata *u) { int work_done = 0; + pa_bool_t checked_left_to_play = FALSE; pa_assert(u); pa_sink_assert_ref(u->sink); @@ -315,7 +339,10 @@ static int unix_write(struct userdata *u) { return r; } - check_left_to_play(u, n); + if (!checked_left_to_play) { + check_left_to_play(u, n); + checked_left_to_play = TRUE; + } if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) return work_done; @@ -862,6 +889,8 @@ static int process_rewind(struct userdata *u) { u->frame_index -= out_frames; pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); pa_sink_process_rewind(u->sink, rewind_nbytes); + + u->after_rewind = TRUE; } } else pa_log_debug("Mhmm, actually there is nothing to rewind."); @@ -938,6 +967,7 @@ static void thread_func(void *userdata) { } u->first = FALSE; + u->after_rewind = FALSE; } else if (u->use_tsched) @@ -1071,6 +1101,7 @@ int pa__init(pa_module*m) { u->use_mmap = use_mmap; u->use_tsched = use_tsched; u->first = TRUE; + u->after_rewind = FALSE; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->alsa_rtpoll_item = NULL; diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 3999ada7..3c6b8dba 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -95,6 +95,8 @@ static const char* const valid_modargs[] = { #define DEFAULT_DEVICE "default" #define DEFAULT_TSCHED_BUFFER_USEC (5*PA_USEC_PER_SEC) #define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) +#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ struct userdata { pa_core *core; @@ -133,12 +135,29 @@ struct userdata { static void fix_tsched_watermark(struct userdata *u) { size_t max_use; + size_t min_sleep, min_wakeup; pa_assert(u); max_use = u->hwbuf_size - u->hwbuf_unused_frames * u->frame_size; - if (u->tsched_watermark >= max_use-u->frame_size) - u->tsched_watermark = max_use-u->frame_size; + min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->source->sample_spec); + min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->source->sample_spec); + + if (min_sleep > max_use/2) + min_sleep = pa_frame_align(max_use/2, &u->source->sample_spec); + if (min_sleep < u->frame_size) + min_sleep = u->frame_size; + + if (min_wakeup > max_use/2) + min_wakeup = pa_frame_align(max_use/2, &u->source->sample_spec); + if (min_wakeup < u->frame_size) + min_wakeup = u->frame_size; + + if (u->tsched_watermark > max_use-min_sleep) + u->tsched_watermark = max_use-min_sleep; + + if (u->tsched_watermark < min_wakeup) + u->tsched_watermark = min_wakeup; } static int try_recover(struct userdata *u, const char *call, int err) { @@ -193,6 +212,7 @@ static void check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { static int mmap_read(struct userdata *u) { int work_done = 0; + pa_bool_t checked_left_to_record = FALSE; pa_assert(u); pa_source_assert_ref(u->source); @@ -217,7 +237,10 @@ static int mmap_read(struct userdata *u) { return r; } - check_left_to_record(u, n); + if (checked_left_to_record) { + check_left_to_record(u, n); + checked_left_to_record = TRUE; + } if (PA_UNLIKELY(n <= 0)) return work_done; @@ -280,6 +303,7 @@ static int mmap_read(struct userdata *u) { static int unix_read(struct userdata *u) { int work_done = 0; + pa_bool_t checked_left_to_record = FALSE; pa_assert(u); pa_source_assert_ref(u->source); @@ -302,7 +326,10 @@ static int unix_read(struct userdata *u) { return r; } - check_left_to_record(u, n); + if (checked_left_to_record) { + check_left_to_record(u, n); + checked_left_to_record = TRUE; + } if (PA_UNLIKELY(n <= 0)) return work_done; -- cgit From b12b8ee16071305cc0686a0d8130f958742a5078 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 18:56:14 +0000 Subject: save errno before calling free() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2313 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/xmalloc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pulse/xmalloc.c b/src/pulse/xmalloc.c index 28490975..a64761bf 100644 --- a/src/pulse/xmalloc.c +++ b/src/pulse/xmalloc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -123,8 +124,12 @@ char *pa_xstrndup(const char *s, size_t l) { } void pa_xfree(void *p) { + int saved_errno; + if (!p) return; + saved_errno = errno; free(p); + errno = saved_errno; } -- cgit From 06a05bc66c46550dd550a8960e421b5e9cb1b2bb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 18:57:18 +0000 Subject: a bit of pa_bool_t'ization git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2314 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/daemon/cpulimit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c index 620a93a6..579b91e3 100644 --- a/src/daemon/cpulimit.c +++ b/src/daemon/cpulimit.c @@ -82,7 +82,7 @@ static pa_io_event *io_event = NULL; static struct sigaction sigaction_prev; /* Nonzero after pa_cpu_limit_init() */ -static int installed = 0; +static pa_bool_t installed = FALSE; /* The current state of operation */ static enum { @@ -210,7 +210,7 @@ int pa_cpu_limit_init(pa_mainloop_api *m) { return -1; } - installed = 1; + installed = TRUE; reset_cpu_time(CPUTIME_INTERVAL_SOFT); @@ -231,7 +231,7 @@ void pa_cpu_limit_done(void) { if (installed) { pa_assert_se(sigaction(SIGXCPU, &sigaction_prev, NULL) >= 0); - installed = 0; + installed = FALSE; } } -- cgit From 563f4b63cd37794b72af4601796edb5b6f5f2455 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 18:59:25 +0000 Subject: make check for $DISPLAY=="" more readable, pa_bool_tization git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2315 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/client-conf-x11.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pulse/client-conf-x11.c b/src/pulse/client-conf-x11.c index e240ba88..49df4b6c 100644 --- a/src/pulse/client-conf-x11.c +++ b/src/pulse/client-conf-x11.c @@ -46,7 +46,10 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { pa_assert(c); - if (!dname && (!(dname = getenv("DISPLAY")) || *dname == '\0')) + if (!dname && !(dname = getenv("DISPLAY"))) + goto finish; + + if (*dname == 0) goto finish; if (!(d = XOpenDisplay(dname))) { @@ -80,7 +83,7 @@ int pa_client_conf_from_x11(pa_client_conf *c, const char *dname) { pa_assert(sizeof(cookie) == sizeof(c->cookie)); memcpy(c->cookie, cookie, sizeof(cookie)); - c->cookie_valid = 1; + c->cookie_valid = TRUE; pa_xfree(c->cookie_file); c->cookie_file = NULL; -- cgit From f49df7a3b09957585d4227390d4ba0929e7e29f2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:04:42 +0000 Subject: * Increase history set to 64 to simplify reduction of indexes * Decrease memory consumption a bit by using bitfields for some bools * Rework reduction code * Drop an unnessacary counter * Before adding a new entry to the history, try to figure out if we already have an existing entry with the same x value and replace that. This fixes a division by zero * Fix up input x for all functions, according to the time offset git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2316 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/time-smoother.c | 74 ++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index b3f8edc5..97e32b73 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -34,7 +34,7 @@ #include "time-smoother.h" -#define HISTORY_MAX 50 +#define HISTORY_MAX 64 /* * Implementation of a time smoothing algorithm to synchronize remote @@ -61,7 +61,6 @@ struct pa_smoother { pa_usec_t adjust_time, history_time; - pa_bool_t monotonic; pa_usec_t time_offset; @@ -82,7 +81,9 @@ struct pa_smoother { double a, b, c; pa_bool_t abc_valid; - pa_bool_t paused; + pa_bool_t monotonic:1; + pa_bool_t paused:1; + pa_usec_t pause_time; }; @@ -122,39 +123,58 @@ void pa_smoother_free(pa_smoother* s) { pa_xfree(s); } +#define REDUCE(x) \ + do { \ + x = (x) % HISTORY_MAX; \ + } while(FALSE) + +#define REDUCE_INC(x) \ + do { \ + x = ((x)+1) % HISTORY_MAX; \ + } while(FALSE) + + static void drop_old(pa_smoother *s, pa_usec_t x) { - unsigned j; - /* First drop items from history which are too old, but make sure - * to always keep two entries in the history */ + /* Drop items from history which are too old, but make sure to + * always keep two entries in the history */ - for (j = s->n_history; j > 2; j--) { + while (s->n_history > 2) { - if (s->history_x[s->history_idx] + s->history_time >= x) { + if (s->history_x[s->history_idx] + s->history_time >= x) /* This item is still valid, and thus all following ones * are too, so let's quit this loop */ break; - } /* Item is too old, let's drop it */ - s->history_idx ++; - while (s->history_idx >= HISTORY_MAX) - s->history_idx -= HISTORY_MAX; + REDUCE_INC(s->history_idx); s->n_history --; } } static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) { - unsigned j; + unsigned j, i; pa_assert(s); + /* First try to update an existing history entry */ + i = s->history_idx; + for (j = s->n_history; j > 0; j--) { + + if (s->history_x[i] == x) { + s->history_y[i] = y; + return; + } + + REDUCE_INC(i); + } + + /* Drop old entries */ drop_old(s, x); /* Calculate position for new entry */ j = s->history_idx + s->n_history; - while (j >= HISTORY_MAX) - j -= HISTORY_MAX; + REDUCE(j); /* Fill in entry */ s->history_x[j] = x; @@ -166,6 +186,7 @@ static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) { /* And make sure we don't store more entries than fit in */ if (s->n_history >= HISTORY_MAX) { s->history_idx += s->n_history - HISTORY_MAX; + REDUCE(s->history_idx); s->n_history = HISTORY_MAX; } } @@ -185,9 +206,7 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) { ay += s->history_y[i]; c++; - i++; - while (i >= HISTORY_MAX) - i -= HISTORY_MAX; + REDUCE_INC(i); } /* Too few measurements, assume gradient of 1 */ @@ -210,14 +229,12 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) { k += dx*dy; t += dx*dx; - i++; - while (i >= HISTORY_MAX) - i -= HISTORY_MAX; + REDUCE_INC(i); } r = (double) k / t; - return s->monotonic && r < 0 ? 0 : r; + return (s->monotonic && r < 0) ? 0 : r; } static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { @@ -305,14 +322,12 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { double nde; pa_assert(s); - pa_assert(x >= s->time_offset); /* Fix up x value */ if (s->paused) x = s->pause_time; - pa_assert(x >= s->time_offset); - x -= s->time_offset; + x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0; pa_assert(x >= s->ex); @@ -340,15 +355,12 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { pa_usec_t y; pa_assert(s); - pa_assert(x >= s->time_offset); /* Fix up x value */ if (s->paused) x = s->pause_time; - pa_assert(x >= s->time_offset); - x -= s->time_offset; - + x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0; pa_assert(x >= s->ex); estimate(s, x, &y, NULL); @@ -397,14 +409,12 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) double nde; pa_assert(s); - pa_assert(x >= s->time_offset); /* Fix up x value */ if (s->paused) x = s->pause_time; - pa_assert(x >= s->time_offset); - x -= s->time_offset; + x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0; pa_assert(x >= s->ex); -- cgit From 264385aae4683a47b15aa37a3949796be72870d1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:06:15 +0000 Subject: strip CRLF line breaks from read CLI commands. This should fix the cli interface for people accessing it via telnet. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2317 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/ioline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c index 860a6511..6d11e4fc 100644 --- a/src/pulsecore/ioline.c +++ b/src/pulsecore/ioline.c @@ -247,7 +247,7 @@ static void scan_for_lines(pa_ioline *l, size_t skip) { l->rbuf_index = 0; if (l->callback) - l->callback(l, p, l->userdata); + l->callback(l, pa_strip_nl(p), l->userdata); skip = 0; } -- cgit From 9dd8f6ceb7cce6576eef2f3278cea16ef76344b9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:09:03 +0000 Subject: add new function pa_sample_clamp() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2318 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sample-util.c | 31 +++++++++++++++++++++++++++++++ src/pulsecore/sample-util.h | 2 ++ 2 files changed, 33 insertions(+) diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index bf1f7c26..654c3b9f 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -1000,3 +1000,34 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, return ret; } + +void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) { + + const float *s; + float *d; + + if (format != PA_SAMPLE_FLOAT32BE && format != PA_SAMPLE_FLOAT32LE) + return; + + s = src; + d = dst; + + if (format == PA_SAMPLE_FLOAT32NE) { + for (; n > 0; n--) { + *d = PA_CLAMP_UNLIKELY(*s, -1.0, 1.0); + + s = (const float*) ((const uint8_t*) s + sstr); + d = (float*) ((uint8_t*) d + dstr); + } + } else + for (; n > 0; n--) { + float f; + + f = PA_FLOAT32_SWAP(*s); + f = PA_CLAMP_UNLIKELY(f, -1.0, 1.0); + *d = PA_FLOAT32_SWAP(f); + + s = (const float*) ((const uint8_t*) s + sstr); + d = (float*) ((uint8_t*) d + dstr); + } +} diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h index 45f00883..59b4c632 100644 --- a/src/pulsecore/sample-util.h +++ b/src/pulsecore/sample-util.h @@ -78,4 +78,6 @@ int pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE; void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n); void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n); +void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n); + #endif -- cgit From b70edf77dad853ebed66e3c07d16bc2c2548e294 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:13:53 +0000 Subject: port pa_sample_clamp() to liboil git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2319 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sample-util.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index 654c3b9f..d295742f 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -1013,12 +1013,8 @@ void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const vo d = dst; if (format == PA_SAMPLE_FLOAT32NE) { - for (; n > 0; n--) { - *d = PA_CLAMP_UNLIKELY(*s, -1.0, 1.0); - - s = (const float*) ((const uint8_t*) s + sstr); - d = (float*) ((uint8_t*) d + dstr); - } + const static float minus_one = -1.0, plus_one = 1.0; + oil_clip_f32(dst, dstr, src, sstr, n, &minus_one, &plus_one); } else for (; n > 0; n--) { float f; -- cgit From 18ad6f845eee212f38053ee0ff0f066f20da3a97 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:15:08 +0000 Subject: don't allow overwriting of callback pointers when we're already dead git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2320 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/subscribe.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c index db25f3cf..0c5686b7 100644 --- a/src/pulse/subscribe.c +++ b/src/pulse/subscribe.c @@ -88,6 +88,9 @@ void pa_context_set_subscribe_callback(pa_context *c, pa_context_subscribe_cb_t pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); + if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) + return; + c->subscribe_callback = cb; c->subscribe_userdata = userdata; } -- cgit From 4f99c431d0122ba1256b5f8793adfa86d21e17ce Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:15:49 +0000 Subject: check for $PULSE_INTERNAL before enabling padsp git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2321 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/utils/padsp.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/utils/padsp.c b/src/utils/padsp.c index 6ab022ee..e43a0de2 100644 --- a/src/utils/padsp.c +++ b/src/utils/padsp.c @@ -302,7 +302,6 @@ static int padsp_disabled(void) { if (!sym_resolved) { sym = (int*) dlsym(RTLD_DEFAULT, "__padsp_disabled__"); sym_resolved = 1; - } pthread_mutex_unlock(&func_mutex); @@ -316,7 +315,7 @@ static int dsp_cloak_enable(void) { if (padsp_disabled() & 1) return 0; - if (getenv("PADSP_NO_DSP")) + if (getenv("PADSP_NO_DSP") || getenv("PULSE_INTERNAL")) return 0; return 1; @@ -326,7 +325,7 @@ static int sndstat_cloak_enable(void) { if (padsp_disabled() & 2) return 0; - if (getenv("PADSP_NO_SNDSTAT")) + if (getenv("PADSP_NO_SNDSTAT") || getenv("PULSE_INTERNAL")) return 0; return 1; @@ -336,7 +335,7 @@ static int mixer_cloak_enable(void) { if (padsp_disabled() & 4) return 0; - if (getenv("PADSP_NO_MIXER")) + if (getenv("PADSP_NO_MIXER") || getenv("PULSE_INTERNAL")) return 0; return 1; -- cgit From b93ea1840ccb49f1ee020186cef6f8a147343b34 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:16:25 +0000 Subject: minor reformat git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2322 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/memblock.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index 46537795..33b38745 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -59,7 +59,9 @@ struct pa_memblock { pa_mempool *pool; pa_memblock_type_t type; - pa_bool_t read_only:1, is_silence:1; + + pa_bool_t read_only:1; + pa_bool_t is_silence:1; pa_atomic_ptr_t data; size_t length; -- cgit From 58168711171d823833a8d699090a8238e24ebea1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:16:55 +0000 Subject: save and restore errno in log functions git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2323 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/log.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index c824e84d..a8985783 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef HAVE_SYSLOG_H #include @@ -109,6 +110,7 @@ void pa_log_levelv_meta( const char *e; char *text, *t, *n, *location; + int saved_errno = errno; pa_assert(level < PA_LOG_LEVEL_MAX); pa_assert(format); @@ -116,8 +118,10 @@ void pa_log_levelv_meta( if ((e = getenv(ENV_LOGLEVEL))) maximal_level = atoi(e); - if (level > maximal_level) + if (level > maximal_level) { + errno = saved_errno; return; + } text = pa_vsprintf_malloc(format, ap); @@ -206,6 +210,8 @@ void pa_log_levelv_meta( pa_xfree(text); pa_xfree(location); + + errno = saved_errno; } void pa_log_level_meta( -- cgit From d7cc1f5a12cb1cf29879443fe1d151c3747aea04 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:17:52 +0000 Subject: change pa_rtpoll_set_timer_absolute() to take a pa_usec_t instead of struct timeval git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2324 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/rtpoll.c | 5 ++--- src/pulsecore/rtpoll.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c index c3e76cac..64fa42ad 100644 --- a/src/pulsecore/rtpoll.c +++ b/src/pulsecore/rtpoll.c @@ -502,11 +502,10 @@ static void update_timer(pa_rtpoll *p) { #endif } -void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts) { +void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec) { pa_assert(p); - pa_assert(ts); - p->next_elapse = *ts; + pa_timeval_store(&p->next_elapse, usec); p->timer_enabled = TRUE; update_timer(p); diff --git a/src/pulsecore/rtpoll.h b/src/pulsecore/rtpoll.h index 6d72eb54..16dadbc3 100644 --- a/src/pulsecore/rtpoll.h +++ b/src/pulsecore/rtpoll.h @@ -74,7 +74,7 @@ void pa_rtpoll_install(pa_rtpoll *p); * cleanly. */ int pa_rtpoll_run(pa_rtpoll *f, pa_bool_t wait); -void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, const struct timeval *ts); +void pa_rtpoll_set_timer_absolute(pa_rtpoll *p, pa_usec_t usec); void pa_rtpoll_set_timer_relative(pa_rtpoll *p, pa_usec_t usec); void pa_rtpoll_set_timer_disabled(pa_rtpoll *p); -- cgit From 414f1d956f98e1921a88061dc118795519168d66 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:22:35 +0000 Subject: install gccmacro.h properly, drop unused core-def.h file git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2325 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 10 +++++----- src/pulsecore/core-def.h | 29 ----------------------------- 2 files changed, 5 insertions(+), 34 deletions(-) delete mode 100644 src/pulsecore/core-def.h diff --git a/src/Makefile.am b/src/Makefile.am index 28882085..5cc9546f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -131,8 +131,7 @@ pulseaudio_SOURCES = \ daemon/daemon-conf.c daemon/daemon-conf.h \ daemon/dumpmodules.c daemon/dumpmodules.h \ daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \ - daemon/main.c \ - pulsecore/gccmacro.h + daemon/main.c pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS) pulseaudio_CPPFLAGS = $(AM_CPPFLAGS) @@ -470,7 +469,8 @@ pulseinclude_HEADERS = \ pulse/version.h \ pulse/volume.h \ pulse/xmalloc.h \ - pulse/proplist.h + pulse/proplist.h \ + pulse/gccmacro.h if HAVE_AVAHI pulseinclude_HEADERS += \ @@ -529,7 +529,6 @@ libpulse_la_SOURCES += \ pulsecore/conf-parser.c pulsecore/conf-parser.h \ pulsecore/core-util.c pulsecore/core-util.h \ pulsecore/dynarray.c pulsecore/dynarray.h \ - pulsecore/gccmacro.h \ pulsecore/hashmap.c pulsecore/hashmap.h \ pulsecore/idxset.c pulsecore/idxset.h \ pulsecore/inet_ntop.c pulsecore/inet_ntop.h \ @@ -562,6 +561,8 @@ libpulse_la_SOURCES += \ pulsecore/object.c pulsecore/object.h \ pulsecore/msgobject.c pulsecore/msgobject.h \ pulsecore/once.c pulsecore/once.h \ + pulsecore/rtclock.c pulsecore/rtclock.h \ + pulsecore/time-smoother.c pulsecore/time-smoother.h \ $(PA_THREAD_OBJS) if OS_IS_WIN32 @@ -661,7 +662,6 @@ noinst_HEADERS = \ pulsecore/cli-text.h \ pulsecore/client.h \ pulsecore/core.h \ - pulsecore/core-def.h \ pulsecore/core-scache.h \ pulsecore/core-subscribe.h \ pulsecore/conf-parser.h \ diff --git a/src/pulsecore/core-def.h b/src/pulsecore/core-def.h deleted file mode 100644 index 4bc05137..00000000 --- a/src/pulsecore/core-def.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef foocoredefhfoo -#define foocoredefhfoo - -/* $Id$ */ - -/*** - This file is part of PulseAudio. - - Copyright 2006 Pierre Ossman for Cendio AB - - PulseAudio is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, - or (at your option) any later version. - - PulseAudio is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* FIXME: Remove this shit */ - -#endif -- cgit From 11559a63863fcbbd8a87b8dd8a790aa0ed6f35d1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:24:09 +0000 Subject: parse boolean parameters as boolean instead of int wherever applicable. add new function pa_cli_command_execute_file_stream() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2326 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/cli-command.c | 65 ++++++++++++++++++++++++++++++++------------- src/pulsecore/cli-command.h | 3 +++ 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 7c85224e..b6194f84 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -594,7 +594,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, return -1; } - if (pa_atoi(m, &mute) < 0) { + if ((mute = pa_parse_boolean(m)) < 0) { pa_strbuf_puts(buf, "Failed to parse mute switch.\n"); return -1; } @@ -628,7 +628,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu return -1; } - if (pa_atoi(m, &mute) < 0) { + if ((mute = pa_parse_boolean(m)) < 0) { pa_strbuf_puts(buf, "Failed to parse mute switch.\n"); return -1; } @@ -664,11 +664,11 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf } if (!(v = pa_tokenizer_get(t, 2))) { - pa_strbuf_puts(buf, "You need to specify a volume >= 0. (0 is muted, 0x10000 is normal volume)\n"); + pa_strbuf_puts(buf, "You need to specify a mute switch setting (0/1).\n"); return -1; } - if (pa_atoi(v, &mute) < 0) { + if ((mute = pa_parse_boolean(v)) < 0) { pa_strbuf_puts(buf, "Failed to parse mute switch.\n"); return -1; } @@ -1125,7 +1125,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b return -1; } - if (pa_atoi(m, &suspend) < 0) { + if ((suspend = pa_parse_boolean(m)) < 0) { pa_strbuf_puts(buf, "Failed to parse suspend switch.\n"); return -1; } @@ -1159,7 +1159,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf return -1; } - if (pa_atoi(m, &suspend) < 0) { + if ((suspend = pa_parse_boolean(m)) < 0) { pa_strbuf_puts(buf, "Failed to parse suspend switch.\n"); return -1; } @@ -1188,7 +1188,7 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p return -1; } - if (pa_atoi(m, &suspend) < 0) { + if ((suspend = pa_parse_boolean(m)) < 0) { pa_strbuf_puts(buf, "Failed to parse suspend switch.\n"); return -1; } @@ -1252,7 +1252,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b } pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink))); - pa_strbuf_printf(buf, "set-sink-mute %s %d\n", sink->name, pa_sink_get_mute(sink)); + pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink))); + pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED)); } for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { @@ -1265,7 +1266,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b } pa_strbuf_printf(buf, "set-source-volume %s 0x%03x\n", source->name, pa_cvolume_avg(pa_source_get_volume(source))); - pa_strbuf_printf(buf, "set-source-mute %s %d\n", source->name, pa_source_get_mute(source)); + pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source))); + pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED)); } @@ -1440,16 +1442,45 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo return pa_cli_command_execute_line_stateful(c, s, buf, fail, NULL); } -int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) { +int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail) { char line[1024]; - FILE *f = NULL; int ifstate = IFSTATE_NONE; int ret = -1; + pa_bool_t _fail = TRUE; + + pa_assert(c); + pa_assert(f); + pa_assert(buf); + + if (!fail) + fail = &_fail; + + while (fgets(line, sizeof(line), f)) { + pa_strip_nl(line); + + if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail) + goto fail; + } + + ret = 0; + +fail: + + return ret; +} + +int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail) { + FILE *f = NULL; + int ret = -1; + pa_bool_t _fail = TRUE; pa_assert(c); pa_assert(fn); pa_assert(buf); + if (!fail) + fail = &_fail; + if (!(f = fopen(fn, "r"))) { pa_strbuf_printf(buf, "open('%s') failed: %s\n", fn, pa_cstrerror(errno)); if (!*fail) @@ -1457,13 +1488,7 @@ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_b goto fail; } - while (fgets(line, sizeof(line), f)) { - char *e = line + strcspn(line, linebreak); - *e = 0; - - if (pa_cli_command_execute_line_stateful(c, line, buf, fail, &ifstate) < 0 && *fail) - goto fail; - } + ret = pa_cli_command_execute_file_stream(c, f, buf, fail); ret = 0; @@ -1477,11 +1502,15 @@ fail: int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail) { const char *p; int ifstate = IFSTATE_NONE; + pa_bool_t _fail = TRUE; pa_assert(c); pa_assert(s); pa_assert(buf); + if (!fail) + fail = &_fail; + p = s; while (*p) { size_t l = strcspn(p, linebreak); diff --git a/src/pulsecore/cli-command.h b/src/pulsecore/cli-command.h index c90c8e08..2a923443 100644 --- a/src/pulsecore/cli-command.h +++ b/src/pulsecore/cli-command.h @@ -36,6 +36,9 @@ int pa_cli_command_execute_line(pa_core *c, const char *s, pa_strbuf *buf, pa_bo /* Execute a whole file of CLI commands */ int pa_cli_command_execute_file(pa_core *c, const char *fn, pa_strbuf *buf, pa_bool_t *fail); +/* Execute a whole file of CLI commands */ +int pa_cli_command_execute_file_stream(pa_core *c, FILE *f, pa_strbuf *buf, pa_bool_t *fail); + /* Split the specified string into lines and run pa_cli_command_execute_line() for each. */ int pa_cli_command_execute(pa_core *c, const char *s, pa_strbuf *buf, pa_bool_t *fail); -- cgit From f3cc178b9233dba9b33210bd35e11e20b6b80f1a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:25:24 +0000 Subject: some minor updates git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2327 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/interpol-test.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c index 85a509d4..f894d2f3 100644 --- a/src/tests/interpol-test.c +++ b/src/tests/interpol-test.c @@ -42,10 +42,9 @@ static pa_context *context = NULL; static pa_stream *stream = NULL; static pa_mainloop_api *mainloop_api = NULL; -static void stream_write_cb(pa_stream *p, size_t length, void *userdata) { - +static void stream_write_cb(pa_stream *p, size_t nbytes, void *userdata) { /* Just some silence */ - pa_stream_write(p, pa_xmalloc0(length), length, pa_xfree, 0, PA_SEEK_RELATIVE); + pa_stream_write(p, pa_xmalloc0(nbytes), nbytes, pa_xfree, 0, PA_SEEK_RELATIVE); } /* This is called whenever the context status changes */ @@ -63,7 +62,7 @@ static void context_state_callback(pa_context *c, void *userdata) { static const pa_sample_spec ss = { .format = PA_SAMPLE_S16LE, .rate = 44100, - .channels = 1 + .channels = 2 }; fprintf(stderr, "Connection established.\n"); @@ -112,9 +111,10 @@ int main(int argc, char *argv[]) { pa_threaded_mainloop_start(m); for (k = 0; k < 5000; k++) { - int success = 0, changed = 0; + pa_bool_t success = FALSE, changed = FALSE; pa_usec_t t, rtc; struct timeval now, tv; + pa_bool_t playing = FALSE; pa_threaded_mainloop_lock(m); @@ -122,22 +122,26 @@ int main(int argc, char *argv[]) { const pa_timing_info *info; if (pa_stream_get_time(stream, &t) >= 0) - success = 1; + success = TRUE; - if ((info = pa_stream_get_timing_info(stream))) - if (last_info.tv_usec != info->timestamp.tv_usec || last_info.tv_sec != info->timestamp.tv_sec) { - changed = 1; + if ((info = pa_stream_get_timing_info(stream))) { + if (memcmp(&last_info, &info->timestamp, sizeof(struct timeval))) { + changed = TRUE; last_info = info->timestamp; } + if (info->playing) + playing = TRUE; + } } pa_threaded_mainloop_unlock(m); - if (success) { - pa_gettimeofday(&now); + pa_gettimeofday(&now); + if (success) { rtc = pa_timeval_diff(&now, &start); - printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\n", k, (unsigned long long) rtc, (unsigned long long) t, (unsigned long long) (rtc-old_rtc), (unsigned long long) (t-old_t), changed); + printf("%i\t%llu\t%llu\t%llu\t%llu\t%u\t%u\n", k, (unsigned long long) rtc, (unsigned long long) t, (unsigned long long) (rtc-old_rtc), (unsigned long long) (t-old_t), changed, playing); + fflush(stdout); old_t = t; old_rtc = rtc; } -- cgit From f94fae3da3e3201afc060d3ae24c96fd9bba56ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:26:41 +0000 Subject: move unlinking code to operation_unlink() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2328 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/operation.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/pulse/operation.c b/src/pulse/operation.c index 5d2da5b8..6b5c142a 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -77,6 +77,23 @@ void pa_operation_unref(pa_operation *o) { } } +static void operation_unlink(pa_operation *o) { + pa_assert(o); + + if (o->context) { + pa_assert(PA_REFCNT_VALUE(o) >= 2); + + PA_LLIST_REMOVE(pa_operation, o->context->operations, o); + pa_operation_unref(o); + + o->context = NULL; + } + + o->stream = NULL; + o->callback = NULL; + o->userdata = NULL; +} + static void operation_set_state(pa_operation *o, pa_operation_state_t st) { pa_assert(o); pa_assert(PA_REFCNT_VALUE(o) >= 1); @@ -88,20 +105,8 @@ static void operation_set_state(pa_operation *o, pa_operation_state_t st) { o->state = st; - if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) { - - if (o->context) { - pa_assert(PA_REFCNT_VALUE(o) >= 2); - - PA_LLIST_REMOVE(pa_operation, o->context->operations, o); - pa_operation_unref(o); - } - - o->context = NULL; - o->stream = NULL; - o->callback = NULL; - o->userdata = NULL; - } + if ((o->state == PA_OPERATION_DONE) || (o->state == PA_OPERATION_CANCELED)) + operation_unlink(o); pa_operation_unref(o); } -- cgit From 52e3628c3edd98ae3402605e7f44a0fc4545dd0a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 1 May 2008 19:51:05 +0000 Subject: Yes, yet another evil all-in-one commit of intervowen changes. I suck. * Drop "state" directory, fold that into "runtime directory" * No longer automatically rewind when a new stream connects * Rework sound file stream, to cause a rewind on initialisation, shorten _pop() code a bit * Fix reference counting of pa_socket_server in the protocol implementations * Rework daemon initialization code to be compatible with non-SUID-root setups where RLIMIT_RTPRIO is non-zero * Print warning if RT/HP is enabled in the config, but due to missing caps, rlimits, policy we cannot enable it. * Fix potential memory leak in pa_open_config_file() * Add pa_find_config_file() which works much like pa_open_config_file() but doesn't actually open the config file in question. Just searches for it. * Add portable pa_is_path_absolute() * Add pa_close_all() and use it on daemon startup to close leaking file descriptors (inspired from what I did for libdaemon) * Add pa_unblock_sigs() and use it on daemon startup to unblock all signals (inspired from libdaemon, too) * Add pa_reset_sigs() and use it on daemon startup to reset all signal handlers (inspired from libdaemon as well) * Implement pa_set_env() * Define RLIMIT_RTTIME and friends if not defined by glibc * Add pa_strempty() * rename state testing macros to include _IS_, to make clearer that they are no states, but testing macros * Implement pa_source_output_set_requested_latency_within_thread() to be able to forward latency info to sources from within the IO thread * Similar for sink inputs * generelize since_underrun counter in sink inputs to "playing_for" and "underrun_for". Use only this for ignore potential rewind requests over underruns * Add new native protocol message PLAYBACK_STREAM_MESSAGE_STARTED for notification about the end of an underrun * Port native protocol to use underrun_for/playing_for which is maintained by the sink input anyway * Pass underrun_for/playing_for in timing info to client * Drop pa_sink_skip() since it breaks underrun detection code * Move PID file and unix sockets to the runtime dir (i.e. ~/.pulse). This fixes a potention DoS attack from other users stealing dirs in /tmp from us so that we cannot take them anymore) * Allow setting of more resource limits from the config file. Set RTTIME by default * Streamline daemon startup code * Rework algorithm to find default configuration files * If run in system mode use "system.pa" instead of "default.pa" as default script file * Change ladspa sink to use pa_clamp_samples() for clamping samples * Teach module-null-sink how to deal with rewinding * Try to support ALSA devices with no implicit channel map. Synthesize one by padding with PA_CHANNEL_POSITION_AUX channels. This is not tested since I lack hardware with these problems. * Make use of time smoother in the client libraries. * Add new pa_stream_is_corked() and pa_stream_set_started_callback() functions to public API * Since our native socket moved, add some code for finding sockets created by old versions of PA. This should ease upgrades git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2329 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/daemon/cmdline.c | 3 +- src/daemon/daemon-conf.c | 178 +++++-- src/daemon/daemon-conf.h | 21 +- src/daemon/daemon.conf.in | 14 +- src/daemon/main.c | 290 ++++++----- src/modules/alsa-util.c | 20 +- src/modules/module-alsa-sink.c | 18 +- src/modules/module-alsa-source.c | 6 +- src/modules/module-combine.c | 31 +- src/modules/module-default-device-restore.c | 154 +++++- src/modules/module-device-restore.c | 9 +- src/modules/module-esound-sink.c | 6 +- src/modules/module-ladspa-sink.c | 53 +- src/modules/module-match.c | 10 +- src/modules/module-null-sink.c | 120 ++++- src/modules/module-oss.c | 22 +- src/modules/module-protocol-stub.c | 70 ++- src/modules/module-remap-sink.c | 39 +- src/modules/module-suspend-on-idle.c | 4 +- src/modules/module-tunnel.c | 14 +- src/modules/module-volume-restore.c | 18 +- src/pulse/client-conf.c | 20 +- src/pulse/context.c | 274 +++++----- src/pulse/def.h | 35 +- src/pulse/internal.h | 47 +- src/pulse/introspect.c | 22 +- src/pulse/scache.c | 4 +- src/pulse/stream.c | 759 ++++++++++++++++++---------- src/pulse/stream.h | 15 +- src/pulsecore/core-util.c | 586 +++++++++++++++++---- src/pulsecore/core-util.h | 41 +- src/pulsecore/native-common.h | 3 + src/pulsecore/pid.c | 20 +- src/pulsecore/protocol-cli.c | 2 +- src/pulsecore/protocol-esound.c | 2 +- src/pulsecore/protocol-http.c | 2 +- src/pulsecore/protocol-native.c | 57 ++- src/pulsecore/protocol-simple.c | 2 +- src/pulsecore/sink-input.c | 170 ++++--- src/pulsecore/sink-input.h | 18 +- src/pulsecore/sink.c | 128 ++--- src/pulsecore/sink.h | 39 +- src/pulsecore/sound-file-stream.c | 59 ++- src/pulsecore/source-output.c | 85 ++-- src/pulsecore/source-output.h | 14 +- src/pulsecore/source.c | 54 +- src/pulsecore/source.h | 5 +- src/utils/pacmd.c | 7 +- 48 files changed, 2373 insertions(+), 1197 deletions(-) diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c index f1e1282c..97c75f37 100644 --- a/src/daemon/cmdline.c +++ b/src/daemon/cmdline.c @@ -293,8 +293,7 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d break; case 'n': - pa_xfree(conf->default_script_file); - conf->default_script_file = NULL; + conf->load_default_script_file = FALSE; break; case ARG_LOG_TARGET: diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index c98c0218..f9ad7ec0 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -45,6 +46,8 @@ #define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "default.pa" #define DEFAULT_SCRIPT_FILE_USER PA_PATH_SEP "default.pa" +#define DEFAULT_SYSTEM_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "system.pa" + #define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PA_PATH_SEP "daemon.conf" #define DEFAULT_CONFIG_FILE_USER PA_PATH_SEP "daemon.conf" @@ -67,6 +70,7 @@ static const pa_daemon_conf default_conf = { .auto_log_target = 1, .script_commands = NULL, .dl_search_path = NULL, + .load_default_script_file = TRUE, .default_script_file = NULL, .log_target = PA_LOG_SYSLOG, .log_level = PA_LOG_NOTICE, @@ -81,34 +85,43 @@ static const pa_daemon_conf default_conf = { .default_fragment_size_msec = 25, .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 } #ifdef HAVE_SYS_RESOURCE_H - , .rlimit_as = { .value = 0, .is_set = FALSE }, - .rlimit_core = { .value = 0, .is_set = FALSE }, + ,.rlimit_fsize = { .value = 0, .is_set = FALSE }, .rlimit_data = { .value = 0, .is_set = FALSE }, - .rlimit_fsize = { .value = 0, .is_set = FALSE }, - .rlimit_nofile = { .value = 256, .is_set = TRUE }, - .rlimit_stack = { .value = 0, .is_set = FALSE } + .rlimit_stack = { .value = 0, .is_set = FALSE }, + .rlimit_core = { .value = 0, .is_set = FALSE }, + .rlimit_rss = { .value = 0, .is_set = FALSE } #ifdef RLIMIT_NPROC - , .rlimit_nproc = { .value = 0, .is_set = FALSE } + ,.rlimit_nproc = { .value = 0, .is_set = FALSE } #endif + ,.rlimit_nofile = { .value = 256, .is_set = TRUE } #ifdef RLIMIT_MEMLOCK - , .rlimit_memlock = { .value = 0, .is_set = FALSE } + ,.rlimit_memlock = { .value = 0, .is_set = FALSE } +#endif + ,.rlimit_as = { .value = 0, .is_set = FALSE } +#ifdef RLIMIT_LOCKS + ,.rlimit_locks = { .value = 0, .is_set = FALSE } +#endif +#ifdef RLIMIT_SIGPENDING + ,.rlimit_sigpending = { .value = 0, .is_set = FALSE } +#endif +#ifdef RLIMIT_MSGQUEUE + ,.rlimit_msgqueue = { .value = 0, .is_set = FALSE } #endif #ifdef RLIMIT_NICE - , .rlimit_nice = { .value = 31, .is_set = TRUE } /* nice level of -11 */ + ,.rlimit_nice = { .value = 31, .is_set = TRUE } /* nice level of -11 */ #endif #ifdef RLIMIT_RTPRIO - , .rlimit_rtprio = { .value = 9, .is_set = TRUE } /* One below JACK's default for the server */ + ,.rlimit_rtprio = { .value = 9, .is_set = TRUE } /* One below JACK's default for the server */ +#endif +#ifdef RLIMIT_RTTIME + ,.rlimit_rttime = { .value = PA_USEC_PER_SEC, .is_set = TRUE } #endif #endif }; pa_daemon_conf* pa_daemon_conf_new(void) { - FILE *f; pa_daemon_conf *c = pa_xnewdup(pa_daemon_conf, &default_conf, 1); - if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file, "r"))) - fclose(f); - c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH); return c; } @@ -412,25 +425,39 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { { "default-fragment-size-msec", parse_fragment_size_msec, NULL }, { "nice-level", parse_nice_level, NULL }, { "disable-remixing", pa_config_parse_bool, NULL }, + { "load-default-script-file", pa_config_parse_bool, NULL }, #ifdef HAVE_SYS_RESOURCE_H - { "rlimit-as", parse_rlimit, NULL }, - { "rlimit-core", parse_rlimit, NULL }, - { "rlimit-data", parse_rlimit, NULL }, { "rlimit-fsize", parse_rlimit, NULL }, - { "rlimit-nofile", parse_rlimit, NULL }, + { "rlimit-data", parse_rlimit, NULL }, { "rlimit-stack", parse_rlimit, NULL }, + { "rlimit-core", parse_rlimit, NULL }, + { "rlimit-rss", parse_rlimit, NULL }, + { "rlimit-nofile", parse_rlimit, NULL }, + { "rlimit-as", parse_rlimit, NULL }, #ifdef RLIMIT_NPROC { "rlimit-nproc", parse_rlimit, NULL }, #endif #ifdef RLIMIT_MEMLOCK { "rlimit-memlock", parse_rlimit, NULL }, #endif +#ifdef RLIMIT_LOCKS + { "rlimit-locks", parse_rlimit, NULL }, +#endif +#ifdef RLIMIT_SIGPENDING + { "rlimit-sigpending", parse_rlimit, NULL }, +#endif +#ifdef RLIMIT_MSGQUEUE + { "rlimit-msgqueue", parse_rlimit, NULL }, +#endif #ifdef RLIMIT_NICE { "rlimit-nice", parse_rlimit, NULL }, #endif #ifdef RLIMIT_RTPRIO { "rlimit-rtprio", parse_rlimit, NULL }, #endif +#ifdef RLIMIT_RTTIME + { "rlimit-rttime", parse_rlimit, NULL }, +#endif #endif { NULL, NULL, NULL }, }; @@ -461,33 +488,66 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { table[23].data = c; table[24].data = c; table[25].data = &c->disable_remixing; + table[26].data = &c->load_default_script_file; #ifdef HAVE_SYS_RESOURCE_H - table[26].data = &c->rlimit_as; - table[27].data = &c->rlimit_core; + table[27].data = &c->rlimit_fsize; table[28].data = &c->rlimit_data; - table[29].data = &c->rlimit_fsize; - table[30].data = &c->rlimit_nofile; - table[31].data = &c->rlimit_stack; + table[29].data = &c->rlimit_stack; + table[30].data = &c->rlimit_as; + table[31].data = &c->rlimit_core; + table[32].data = &c->rlimit_nofile; + table[33].data = &c->rlimit_as; #ifdef RLIMIT_NPROC - table[32].data = &c->rlimit_nproc; + table[34].data = &c->rlimit_nproc; #endif + #ifdef RLIMIT_MEMLOCK #ifndef RLIMIT_NPROC #error "Houston, we have a numbering problem!" #endif - table[33].data = &c->rlimit_memlock; + table[35].data = &c->rlimit_memlock; #endif -#ifdef RLIMIT_NICE + +#ifdef RLIMIT_LOCKS #ifndef RLIMIT_MEMLOCK #error "Houston, we have a numbering problem!" #endif - table[34].data = &c->rlimit_nice; + table[36].data = &c->rlimit_locks; +#endif + +#ifdef RLIMIT_SIGPENDING +#ifndef RLIMIT_LOCKS +#error "Houston, we have a numbering problem!" +#endif + table[37].data = &c->rlimit_sigpending; +#endif + +#ifdef RLIMIT_MSGQUEUE +#ifndef RLIMIT_SIGPENDING +#error "Houston, we have a numbering problem!" +#endif + table[38].data = &c->rlimit_msgqueue; +#endif + +#ifdef RLIMIT_NICE +#ifndef RLIMIT_MSGQUEUE +#error "Houston, we have a numbering problem!" +#endif + table[39].data = &c->rlimit_nice; #endif + #ifdef RLIMIT_RTPRIO #ifndef RLIMIT_NICE #error "Houston, we have a numbering problem!" #endif - table[35].data = &c->rlimit_rtprio; + table[40].data = &c->rlimit_rtprio; +#endif + +#ifdef RLIMIT_RTTIME +#ifndef RLIMIT_RTTIME +#error "Houston, we have a numbering problem!" +#endif + table[41].data = &c->rlimit_rttime; #endif #endif @@ -496,10 +556,10 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { f = filename ? fopen(c->config_file = pa_xstrdup(filename), "r") : - pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file, "r"); + pa_open_config_file(DEFAULT_CONFIG_FILE, DEFAULT_CONFIG_FILE_USER, ENV_CONFIG_FILE, &c->config_file); if (!f && errno != ENOENT) { - pa_log_warn("Failed to open configuration file '%s': %s", c->config_file, pa_cstrerror(errno)); + pa_log_warn("Failed to open configuration file: %s", pa_cstrerror(errno)); goto finish; } @@ -514,6 +574,7 @@ finish: int pa_daemon_conf_env(pa_daemon_conf *c) { char *e; + pa_assert(c); if ((e = getenv(ENV_DL_SEARCH_PATH))) { pa_xfree(c->dl_search_path); @@ -527,6 +588,35 @@ int pa_daemon_conf_env(pa_daemon_conf *c) { return 0; } +const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c) { + pa_assert(c); + + if (!c->default_script_file) { + if (c->system_instance) + c->default_script_file = pa_find_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE); + else + c->default_script_file = pa_find_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE); + } + + return c->default_script_file; +} + +FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c) { + FILE *f; + pa_assert(c); + + if (!c->default_script_file) { + if (c->system_instance) + f = pa_open_config_file(DEFAULT_SYSTEM_SCRIPT_FILE, NULL, ENV_SCRIPT_FILE, &c->default_script_file); + else + f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file); + } else + f = fopen(c->default_script_file, "r"); + + return f; +} + + static const char* const log_level_to_string[] = { [PA_LOG_DEBUG] = "debug", [PA_LOG_INFO] = "info", @@ -561,8 +651,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time); pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time); pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time); - pa_strbuf_printf(s, "dl-search-path = %s\n", c->dl_search_path ? c->dl_search_path : ""); - pa_strbuf_printf(s, "default-script-file = %s\n", c->default_script_file); + pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path)); + pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c))); + pa_strbuf_printf(s, "load-default-script-file = %s\n", pa_yes_no(c->load_default_script_file)); pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr")); pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]); pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method)); @@ -573,23 +664,36 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments); pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec); #ifdef HAVE_SYS_RESOURCE_H - pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1); - pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1); - pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1); pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1); - pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1); + pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1); pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1); + pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1); + pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1); + pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1); #ifdef RLIMIT_NPROC pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1); #endif + pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1); #ifdef RLIMIT_MEMLOCK pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1); #endif +#ifdef RLIMIT_LOCKS + pa_strbuf_printf(s, "rlimit-locks = %li\n", c->rlimit_locks.is_set ? (long int) c->rlimit_locks.value : -1); +#endif +#ifdef RLIMIT_SIGPENDING + pa_strbuf_printf(s, "rlimit-sigpending = %li\n", c->rlimit_sigpending.is_set ? (long int) c->rlimit_sigpending.value : -1); +#endif +#ifdef RLIMIT_MSGQUEUE + pa_strbuf_printf(s, "rlimit-msgqueue = %li\n", c->rlimit_msgqueue.is_set ? (long int) c->rlimit_msgqueue.value : -1); +#endif #ifdef RLIMIT_NICE - pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_nice.value : -1); + pa_strbuf_printf(s, "rlimit-nice = %li\n", c->rlimit_nice.is_set ? (long int) c->rlimit_nice.value : -1); #endif #ifdef RLIMIT_RTPRIO - pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_rtprio.value : -1); + pa_strbuf_printf(s, "rlimit-rtprio = %li\n", c->rlimit_rtprio.is_set ? (long int) c->rlimit_rtprio.value : -1); +#endif +#ifdef RLIMIT_RTTIME + pa_strbuf_printf(s, "rlimit-rttime = %li\n", c->rlimit_rttime.is_set ? (long int) c->rlimit_rttime.value : -1); #endif #endif diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index 3dcafbfe..03a75661 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -27,6 +27,7 @@ #include #include +#include #include #ifdef HAVE_SYS_RESOURCE_H @@ -65,7 +66,8 @@ typedef struct pa_daemon_conf { system_instance, no_cpu_limit, disable_shm, - disable_remixing; + disable_remixing, + load_default_script_file; int exit_idle_time, module_idle_time, scache_idle_time, @@ -79,19 +81,31 @@ typedef struct pa_daemon_conf { char *config_file; #ifdef HAVE_SYS_RESOURCE_H - pa_rlimit rlimit_as, rlimit_core, rlimit_data, rlimit_fsize, rlimit_nofile, rlimit_stack; + pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as; #ifdef RLIMIT_NPROC pa_rlimit rlimit_nproc; #endif #ifdef RLIMIT_MEMLOCK pa_rlimit rlimit_memlock; #endif +#ifdef RLIMIT_LOCKS + pa_rlimit rlimit_locks; +#endif +#ifdef RLIMIT_SIGPENDING + pa_rlimit rlimit_sigpending; +#endif +#ifdef RLIMIT_MSGQUEUE + pa_rlimit rlimit_msgqueue; +#endif #ifdef RLIMIT_NICE pa_rlimit rlimit_nice; #endif #ifdef RLIMIT_RTPRIO pa_rlimit rlimit_rtprio; #endif +#ifdef RLIMIT_RTTIME + pa_rlimit rlimit_rttime; +#endif #endif unsigned default_n_fragments, default_fragment_size_msec; @@ -121,4 +135,7 @@ int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string); int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string); int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string); +const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c); +FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c); + #endif diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index e4cfb82b..fd35c0f6 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -40,6 +40,7 @@ ; dl-search-path = (depends on architecture) +; load-defaul-script-file = yes ; default-script-file = @PA_DEFAULT_CONFIG_FILE@ ; log-target = auto @@ -50,16 +51,21 @@ ; no-cpu-limit = no -; rlimit-as = -1 -; rlimit-core = -1 -; rlimit-data = -1 ; rlimit-fsize = -1 -; rlimit-nofile = 256 +; rlimit-data = -1 ; rlimit-stack = -1 +; rlimit-core = -1 +; rlimit-as = -1 +; rlimit-rss = -1 ; rlimit-nproc = -1 +; rlimit-nofile = 256 ; rlimit-memlock = -1 +; rlimit-locks = -1 +; rlimit-sigpending = -1 +; rlimit-msgqueue = -1 ; rlimit-nice = 31 ; rlimit-rtprio = 9 +; rlimit-rtttime = 1000000 ; default-sample-format = s16le ; default-sample-rate = 44100 diff --git a/src/daemon/main.c b/src/daemon/main.c index 6b0c81da..b1ba5a31 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -115,7 +115,7 @@ static void message_cb(pa_mainloop_api*a, pa_time_event*e, PA_GCC_UNUSED const s MSG msg; struct timeval tvnext; - while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) raise(SIGTERM); else { @@ -164,8 +164,6 @@ static void signal_callback(pa_mainloop_api*m, PA_GCC_UNUSED pa_signal_event *e, } } -#define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value))) - #if defined(HAVE_PWD_H) && defined(HAVE_GRP_H) static int change_user(void) { @@ -241,14 +239,14 @@ static int change_user(void) { return -1; } - set_env("USER", PA_SYSTEM_USER); - set_env("USERNAME", PA_SYSTEM_USER); - set_env("LOGNAME", PA_SYSTEM_USER); - set_env("HOME", PA_SYSTEM_RUNTIME_PATH); + pa_set_env("USER", PA_SYSTEM_USER); + pa_set_env("USERNAME", PA_SYSTEM_USER); + pa_set_env("LOGNAME", PA_SYSTEM_USER); + pa_set_env("HOME", PA_SYSTEM_RUNTIME_PATH); /* Relevant for pa_runtime_path() */ - set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH); - set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH); + pa_set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH); + pa_set_env("PULSE_CONFIG_PATH", PA_SYSTEM_RUNTIME_PATH); pa_log_info("Successfully dropped root privileges."); @@ -264,23 +262,6 @@ static int change_user(void) { #endif /* HAVE_PWD_H && HAVE_GRP_H */ -static int create_runtime_dir(void) { - char fn[PATH_MAX]; - - pa_runtime_path(NULL, fn, sizeof(fn)); - - /* This function is called only when the daemon is started in - * per-user mode. We create the runtime directory somewhere in - * /tmp/ with the current UID/GID */ - - if (pa_make_secure_dir(fn, 0700, (uid_t)-1, (gid_t)-1) < 0) { - pa_log("Failed to create '%s': %s", fn, pa_cstrerror(errno)); - return -1; - } - - return 0; -} - #ifdef HAVE_SYS_RESOURCE_H static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) { @@ -293,7 +274,7 @@ static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) { rl.rlim_cur = rl.rlim_max = r->value; if (setrlimit(resource, &rl) < 0) { - pa_log_warn("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno)); + pa_log_info("setrlimit(%s, (%u, %u)) failed: %s", name, (unsigned) r->value, (unsigned) r->value, pa_cstrerror(errno)); return -1; } @@ -301,17 +282,27 @@ static int set_one_rlimit(const pa_rlimit *r, int resource, const char *name) { } static void set_all_rlimits(const pa_daemon_conf *conf) { - set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS"); - set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE"); - set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA"); set_one_rlimit(&conf->rlimit_fsize, RLIMIT_FSIZE, "RLIMIT_FSIZE"); - set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE"); + set_one_rlimit(&conf->rlimit_data, RLIMIT_DATA, "RLIMIT_DATA"); set_one_rlimit(&conf->rlimit_stack, RLIMIT_STACK, "RLIMIT_STACK"); + set_one_rlimit(&conf->rlimit_core, RLIMIT_CORE, "RLIMIT_CORE"); + set_one_rlimit(&conf->rlimit_rss, RLIMIT_RSS, "RLIMIT_RSS"); #ifdef RLIMIT_NPROC set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC"); #endif + set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE"); #ifdef RLIMIT_MEMLOCK set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK"); +#endif + set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS"); +#ifdef RLIMIT_LOCKS + set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS"); +#endif +#ifdef RLIMIT_SIGPENDING + set_one_rlimit(&conf->rlimit_sigpending, RLIMIT_SIGPENDING, "RLIMIT_SIGPENDING"); +#endif +#ifdef RLIMIT_MSGQUEUE + set_one_rlimit(&conf->rlimit_msgqueue, RLIMIT_MSGQUEUE, "RLIMIT_MSGQUEUE"); #endif #ifdef RLIMIT_NICE set_one_rlimit(&conf->rlimit_nice, RLIMIT_NICE, "RLIMIT_NICE"); @@ -319,6 +310,9 @@ static void set_all_rlimits(const pa_daemon_conf *conf) { #ifdef RLIMIT_RTPRIO set_one_rlimit(&conf->rlimit_rtprio, RLIMIT_RTPRIO, "RLIMIT_RTPRIO"); #endif +#ifdef RLIMIT_RTTIME + set_one_rlimit(&conf->rlimit_rttime, RLIMIT_RTTIME, "RLIMIT_RTTIME"); +#endif } #endif @@ -329,19 +323,20 @@ int main(int argc, char *argv[]) { pa_mainloop *mainloop = NULL; char *s; int r = 0, retval = 1, d = 0; - int daemon_pipe[2] = { -1, -1 }; pa_bool_t suid_root, real_root; - int valid_pid_file = 0; + pa_bool_t valid_pid_file = FALSE; gid_t gid = (gid_t) -1; - pa_bool_t allow_realtime, allow_high_priority; pa_bool_t ltdl_init = FALSE; - + int passed_fd = -1; + const char *e; +#ifdef HAVE_FORK + int daemon_pipe[2] = { -1, -1 }; +#endif #ifdef OS_IS_WIN32 - pa_time_event *timer; - struct timeval tv; + pa_time_event *win32_timer; + struct timeval win32_tv; #endif - #if defined(__linux__) && defined(__OPTIMIZE__) /* Disable lazy relocations to make usage of external libraries @@ -355,7 +350,7 @@ int main(int argc, char *argv[]) { /* We have to execute ourselves, because the libc caches the * value of $LD_BIND_NOW on initialization. */ - putenv(pa_xstrdup("LD_BIND_NOW=1")); + pa_set_env("LD_BIND_NOW", "1"); pa_assert_se(rp = pa_readlink("/proc/self/exe")); pa_assert_se(execv(rp, argv) == 0); } @@ -385,6 +380,18 @@ int main(int argc, char *argv[]) { * is just too risky tun let PA run as root all the time. */ } + if ((e = getenv("PULSE_PASSED_FD"))) { + passed_fd = atoi(e); + + if (passed_fd <= 2) + passed_fd = -1; + } + + pa_close_all(passed_fd, -1); + + pa_reset_sigs(-1); + pa_unblock_sigs(-1); + /* At this point, we are a normal user, possibly with CAP_NICE if * we were started SUID. If we are started as normal root, than we * still are normal root. */ @@ -410,67 +417,59 @@ int main(int argc, char *argv[]) { pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL); if (suid_root) { + pa_bool_t allow_realtime, allow_high_priority; + /* Ok, we're suid root, so let's better not enable high prio * or RT by default */ allow_high_priority = allow_realtime = FALSE; + if (conf->high_priority || conf->realtime_scheduling) + if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) { + pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling."); + allow_realtime = conf->realtime_scheduling; + allow_high_priority = conf->high_priority; + } + #ifdef HAVE_POLKIT - if (conf->high_priority) { + if (conf->high_priority && !allow_high_priority) { if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) { - pa_log_info("PolicyKit grants us acquire-high-priority privilige."); + pa_log_info("PolicyKit grants us acquire-high-priority privilege."); allow_high_priority = TRUE; } else - pa_log_info("PolicyKit refuses acquire-high-priority privilige."); + pa_log_info("PolicyKit refuses acquire-high-priority privilege."); } - if (conf->realtime_scheduling) { + if (conf->realtime_scheduling && !allow_realtime) { if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) { - pa_log_info("PolicyKit grants us acquire-real-time privilige."); + pa_log_info("PolicyKit grants us acquire-real-time privilege."); allow_realtime = TRUE; } else - pa_log_info("PolicyKit refuses acquire-real-time privilige."); + pa_log_info("PolicyKit refuses acquire-real-time privilege."); } #endif - if ((conf->high_priority || conf->realtime_scheduling) && pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) { - pa_log_info("We're in the group '"PA_REALTIME_GROUP"', allowing real-time and high-priority scheduling."); - allow_realtime = conf->realtime_scheduling; - allow_high_priority = conf->high_priority; - } - if (!allow_high_priority && !allow_realtime) { /* OK, there's no further need to keep CAP_NICE. Hence * let's give it up early */ pa_drop_caps(); - pa_drop_root(); - suid_root = real_root = FALSE; + suid_root = FALSE; if (conf->high_priority || conf->realtime_scheduling) pa_log_notice("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n" "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n" "For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."); } - - } else { - - /* OK, we're a normal user, so let's allow the user evrything - * he asks for, it's now the kernel's job to enforce limits, - * not ours anymore */ - allow_high_priority = allow_realtime = TRUE; } - if (conf->high_priority && !allow_high_priority) { - pa_log_info("High-priority scheduling enabled in configuration but now allowed by policy. Disabling forcibly."); - conf->high_priority = FALSE; - } +#ifdef HAVE_SYS_RESOURCE_H + set_all_rlimits(conf); +#endif - if (conf->realtime_scheduling && !allow_realtime) { - pa_log_info("Real-time scheduling enabled in configuration but now allowed by policy. Disabling forcibly."); - conf->realtime_scheduling = FALSE; - } + if (conf->high_priority && !pa_can_high_priority()) + pa_log_warn("High-priority scheduling enabled in configuration but now allowed by policy."); if (conf->high_priority && conf->cmd == PA_CMD_DAEMON) pa_raise_priority(conf->nice_level); @@ -482,28 +481,38 @@ int main(int argc, char *argv[]) { #ifdef RLIMIT_RTPRIO if (!drop) { - + struct rlimit rl; /* At this point we still have CAP_NICE if we were loaded * SUID root. If possible let's acquire RLIMIT_RTPRIO * instead and give CAP_NICE up. */ - const pa_rlimit rl = { 9, TRUE }; + if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) { - if (set_one_rlimit(&rl, RLIMIT_RTPRIO, "RLIMIT_RTPRIO") >= 0) { - pa_log_info("Successfully increased RLIMIT_RTPRIO, giving up CAP_NICE."); - drop = TRUE; - } else - pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno)); + if (rl.rlim_cur >= 9) + drop = TRUE; + else { + rl.rlim_max = rl.rlim_cur = 9; + + if (setrlimit(RLIMIT_RTPRIO, &rl) < 0) { + pa_log_info("Successfully increased RLIMIT_RTPRIO"); + drop = TRUE; + } else + pa_log_warn("RLIMIT_RTPRIO failed: %s", pa_cstrerror(errno)); + } + } } #endif if (drop) { + pa_log_info("Giving up CAP_NICE"); pa_drop_caps(); - pa_drop_root(); - suid_root = real_root = FALSE; + suid_root = FALSE; } } + if (conf->realtime_scheduling && !pa_can_realtime()) + pa_log_warn("Real-time scheduling enabled in configuration but now allowed by policy."); + LTDL_SET_PRELOADED_SYMBOLS(); pa_ltdl_init(); ltdl_init = TRUE; @@ -605,7 +614,7 @@ int main(int argc, char *argv[]) { #ifdef HAVE_FORK if (pipe(daemon_pipe) < 0) { - pa_log("Failed to create pipe."); + pa_log("pipe failed: %s", pa_cstrerror(errno)); goto finish; } @@ -615,20 +624,24 @@ int main(int argc, char *argv[]) { } if (child != 0) { + ssize_t n; /* Father */ pa_assert_se(pa_close(daemon_pipe[1]) == 0); daemon_pipe[1] = -1; - if (pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL) != sizeof(retval)) { - pa_log("read() failed: %s", pa_cstrerror(errno)); + if ((n = pa_loop_read(daemon_pipe[0], &retval, sizeof(retval), NULL)) != sizeof(retval)) { + + if (n < 0) + pa_log("read() failed: %s", pa_cstrerror(errno)); + retval = 1; } if (retval) - pa_log("daemon startup failed."); + pa_log("Daemon startup failed."); else - pa_log_info("daemon startup successful."); + pa_log_info("Daemon startup successful."); goto finish; } @@ -652,9 +665,9 @@ int main(int argc, char *argv[]) { pa_close(1); pa_close(2); - open("/dev/null", O_RDONLY); - open("/dev/null", O_WRONLY); - open("/dev/null", O_WRONLY); + pa_assert_se(open("/dev/null", O_RDONLY) == 0); + pa_assert_se(open("/dev/null", O_WRONLY) == 1); + pa_assert_se(open("/dev/null", O_WRONLY) == 2); #else FreeConsole(); #endif @@ -677,39 +690,32 @@ int main(int argc, char *argv[]) { #endif } + pa_set_env("PULSE_INTERNAL", "1"); pa_assert_se(chdir("/") == 0); umask(0022); - if (conf->system_instance) { + if (conf->system_instance) if (change_user() < 0) goto finish; - } else if (create_runtime_dir() < 0) - goto finish; + + pa_log_info("This is PulseAudio " PACKAGE_VERSION); + pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE); + pa_log_info("Using runtime directory %s.", s = pa_get_runtime_dir()); + pa_xfree(s); if (conf->use_pid_file) { if (pa_pid_file_create() < 0) { pa_log("pa_pid_file_create() failed."); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); -#endif goto finish; } - valid_pid_file = 1; + valid_pid_file = TRUE; } -#ifdef HAVE_SYS_RESOURCE_H - set_all_rlimits(conf); -#endif - #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif - pa_log_info("This is PulseAudio " PACKAGE_VERSION); - pa_log_info("Page size is %lu bytes", (unsigned long) PA_PAGE_SIZE); - if (pa_rtclock_hrtimer()) pa_log_info("Fresh high-resolution timers available! Bon appetit!"); else @@ -738,11 +744,11 @@ int main(int argc, char *argv[]) { c->realtime_priority = conf->realtime_priority; c->realtime_scheduling = !!conf->realtime_scheduling; c->disable_remixing = !!conf->disable_remixing; + c->running_as_daemon = !!conf->daemonize; pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0); pa_signal_new(SIGINT, signal_callback, c); pa_signal_new(SIGTERM, signal_callback, c); - #ifdef SIGUSR1 pa_signal_new(SIGUSR1, signal_callback, c); #endif @@ -754,23 +760,27 @@ int main(int argc, char *argv[]) { #endif #ifdef OS_IS_WIN32 - pa_assert_se(timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&tv), message_cb, NULL)); + win32_timer = pa_mainloop_get_api(mainloop)->time_new(pa_mainloop_get_api(mainloop), pa_gettimeofday(&win32_tv), message_cb, NULL); #endif - if (conf->daemonize) - c->running_as_daemon = TRUE; - oil_init(); if (!conf->no_cpu_limit) pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0); buf = pa_strbuf_new(); - if (conf->default_script_file) - r = pa_cli_command_execute_file(c, conf->default_script_file, buf, &conf->fail); + if (conf->load_default_script_file) { + FILE *f; + + if ((f = pa_daemon_conf_open_default_script_file(conf))) { + r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail); + fclose(f); + } + } if (r >= 0) r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail); + pa_log_error("%s", s = pa_strbuf_tostring_free(buf)); pa_xfree(s); @@ -780,53 +790,55 @@ int main(int argc, char *argv[]) { if (r < 0 && conf->fail) { pa_log("Failed to initialize daemon."); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); -#endif - } else if (!c->modules || pa_idxset_size(c->modules) == 0) { - pa_log("daemon startup without any loaded modules, refusing to work."); -#ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); -#endif - } else { + goto finish; + } - retval = 0; + if (!c->modules || pa_idxset_size(c->modules) == 0) { + pa_log("Daemon startup without any loaded modules, refusing to work."); + goto finish; + } + + if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) { + pa_log_error("Default sink name (%s) does not exist in name register.", c->default_sink_name); + goto finish; + } - if (c->default_sink_name && - pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, 1) == NULL) { - pa_log_error("%s : Default sink name (%s) does not exist in name register.", __FILE__, c->default_sink_name); - retval = !!conf->fail; - } #ifdef HAVE_FORK - if (conf->daemonize) - pa_loop_write(daemon_pipe[1], &retval, sizeof(retval), NULL); + if (conf->daemonize) { + int ok = 0; + pa_loop_write(daemon_pipe[1], &ok, sizeof(ok), NULL); + } #endif - if (!retval) { - pa_log_info("Daemon startup complete."); - if (pa_mainloop_run(mainloop, &retval) < 0) - retval = 1; - pa_log_info("Daemon shutdown initiated."); - } - } + pa_log_info("Daemon startup complete."); + + retval = 0; + if (pa_mainloop_run(mainloop, &retval) < 0) + goto finish; + + pa_log_info("Daemon shutdown initiated."); + +finish: #ifdef OS_IS_WIN32 - pa_mainloop_get_api(mainloop)->time_free(timer); + if (win32_timer) + pa_mainloop_get_api(mainloop)->time_free(win32_timer); #endif - pa_core_unref(c); + if (c) { + pa_core_unref(c); + pa_log_info("Daemon terminated."); + } if (!conf->no_cpu_limit) pa_cpu_limit_done(); pa_signal_done(); - pa_log_info("Daemon terminated."); - -finish: +#ifdef HAVE_FORK + pa_close_pipe(daemon_pipe); +#endif if (mainloop) pa_mainloop_free(mainloop); @@ -837,8 +849,6 @@ finish: if (valid_pid_file) pa_pid_file_remove(); - pa_close_pipe(daemon_pipe); - #ifdef OS_IS_WIN32 WSACleanup(); #endif diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 0c4c020b..47ed9ace 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -671,8 +671,24 @@ snd_pcm_t *pa_alsa_open_by_device_string( *dev = d; if (ss->channels != map->channels) { - pa_assert_se(pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_AUX)); - pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA); + if (!pa_channel_map_init_auto(map, ss->channels, PA_CHANNEL_MAP_ALSA)) { + unsigned c; + pa_channel_position_t pos; + + pa_log_warn("Device has an unknown channel mapping. This is a limitation of ALSA. Synthesizing channel map."); + + for (c = ss->channels; c > 0; c--) + if (pa_channel_map_init_auto(map, c, PA_CHANNEL_MAP_ALSA)) + break; + + pa_assert(c > 0); + + pos = PA_CHANNEL_POSITION_AUX0; + for (; c < map->channels; c ++) + map->map[c] = pos++; + + map->channels = ss->channels; + } } return pcm_handle; diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 5cc27f11..efb0fd8a 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -665,7 +665,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { case PA_SINK_SUSPENDED: - pa_assert(PA_SINK_OPENED(u->sink->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); if (suspend(u) < 0) return -1; @@ -836,9 +836,20 @@ static int sink_set_mute_cb(pa_sink *s) { static void sink_update_requested_latency_cb(pa_sink *s) { struct userdata *u = s->userdata; + snd_pcm_sframes_t before; pa_assert(u); + before = u->hwbuf_unused_frames; update_sw_params(u); + + /* Let's check whether we now use only a smaller part of the + buffer then before. If so, we need to make sure that subsequent + rewinds are relative to the new maxium fill level and not to the + current fill level. Thus, let's do a full rewind once, to clear + things up. */ + + if (u->hwbuf_unused_frames > before) + pa_sink_request_rewind(s, 0); } static int process_rewind(struct userdata *u) { @@ -846,6 +857,7 @@ static int process_rewind(struct userdata *u) { size_t rewind_nbytes, unused_nbytes, limit_nbytes; pa_assert(u); + /* Figure out how much we shall rewind and reset the counter */ rewind_nbytes = u->sink->thread_info.rewind_nbytes; u->sink->thread_info.rewind_nbytes = 0; @@ -917,7 +929,7 @@ static void thread_func(void *userdata) { /* pa_log_debug("loop"); */ /* Render some data and write it to the dsp */ - if (PA_SINK_OPENED(u->sink->thread_info.state)) { + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { int work_done = 0; if (u->sink->thread_info.rewind_nbytes > 0) @@ -982,7 +994,7 @@ static void thread_func(void *userdata) { goto finish; /* Tell ALSA about this and process its response */ - if (PA_SINK_OPENED(u->sink->thread_info.state)) { + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { struct pollfd *pollfd; unsigned short revents = 0; int err; diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 3c6b8dba..9eb6f06b 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -621,7 +621,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { case PA_SOURCE_SUSPENDED: - pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state)); + pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state)); if (suspend(u) < 0) return -1; @@ -819,7 +819,7 @@ static void thread_func(void *userdata) { pa_log_debug("loop"); /* Read some data and pass it to the sources */ - if (PA_SOURCE_OPENED(u->source->thread_info.state)) { + if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { int work_done = 0; if (u->use_mmap) @@ -867,7 +867,7 @@ static void thread_func(void *userdata) { goto finish; /* Tell ALSA about this and process its response */ - if (PA_SOURCE_OPENED(u->source->thread_info.state)) { + if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { struct pollfd *pollfd; unsigned short revents = 0; int err; diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index e15654fa..2409ef8f 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -162,13 +162,13 @@ static void adjust_rates(struct userdata *u) { if (!u->master) return; - if (!PA_SINK_OPENED(pa_sink_get_state(u->sink))) + if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink))) return; for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { pa_usec_t sink_latency; - if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink))) + if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) continue; sink_latency = pa_sink_get_latency(o->sink); @@ -194,7 +194,7 @@ static void adjust_rates(struct userdata *u) { for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) { uint32_t r = base_rate; - if (!o->sink_input || !PA_SINK_OPENED(pa_sink_get_state(o->sink))) + if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) continue; if (o->total_latency < target_latency) @@ -258,7 +258,10 @@ static void thread_func(void *userdata) { pa_rtclock_get(&now); if (!u->thread_info.in_null_mode || pa_timeval_cmp(&u->thread_info.timestamp, &now) <= 0) { - pa_sink_skip(u->sink, u->block_size); + pa_memchunk chunk; + + pa_sink_render_full(u->sink, u->block_size, &chunk); + pa_memblock_unref(chunk.memblock); if (!u->thread_info.in_null_mode) u->thread_info.timestamp = now; @@ -432,7 +435,7 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64 case SINK_INPUT_MESSAGE_POST: - if (PA_SINK_OPENED(o->sink_input->sink->thread_info.state)) + if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state)) pa_memblockq_push_align(o->memblockq, chunk); else pa_memblockq_flush(o->memblockq); @@ -471,7 +474,7 @@ static void enable_output(struct output *o) { pa_sink_input_put(o->sink_input); - if (o->userdata->sink && PA_SINK_LINKED(pa_sink_get_state(o->userdata->sink))) + if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink))) pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL); } } @@ -504,7 +507,7 @@ static void unsuspend(struct userdata *u) { pa_sink_suspend(o->sink, FALSE); - if (PA_SINK_OPENED(pa_sink_get_state(o->sink))) + if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) enable_output(o); } @@ -525,7 +528,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) { switch (state) { case PA_SINK_SUSPENDED: - pa_assert(PA_SINK_OPENED(pa_sink_get_state(u->sink))); + pa_assert(PA_SINK_IS_OPENED(pa_sink_get_state(u->sink))); suspend(u); break; @@ -697,7 +700,7 @@ static void pick_master(struct userdata *u, struct output *except) { if (u->master && u->master != except && u->master->sink_input && - PA_SINK_OPENED(pa_sink_get_state(u->master->sink))) { + PA_SINK_IS_OPENED(pa_sink_get_state(u->master->sink))) { update_master(u, u->master); return; } @@ -705,7 +708,7 @@ static void pick_master(struct userdata *u, struct output *except) { for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) if (o != except && o->sink_input && - PA_SINK_OPENED(pa_sink_get_state(o->sink))) { + PA_SINK_IS_OPENED(pa_sink_get_state(o->sink))) { update_master(u, o); return; } @@ -780,7 +783,7 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0); - if (u->sink && PA_SINK_LINKED(pa_sink_get_state(u->sink))) + if (u->sink && PA_SINK_IS_LINKED(pa_sink_get_state(u->sink))) pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL); else { /* If the sink is not yet started, we need to do the activation ourselves */ @@ -792,10 +795,10 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) { o->outq); } - if (PA_SINK_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) { + if (PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) || pa_sink_get_state(u->sink) == PA_SINK_INIT) { pa_sink_suspend(sink, FALSE); - if (PA_SINK_OPENED(pa_sink_get_state(sink))) + if (PA_SINK_IS_OPENED(pa_sink_get_state(sink))) if (output_create_sink_input(o) < 0) goto fail; } @@ -898,7 +901,7 @@ static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struc state = pa_sink_get_state(s); - if (PA_SINK_OPENED(state) && PA_SINK_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) { + if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input) { enable_output(o); pick_master(u, NULL); } diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c index b550ae78..a7fc3a3f 100644 --- a/src/modules/module-default-device-restore.c +++ b/src/modules/module-default-device-restore.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2006 Lennart Poettering + Copyright 2006-2008 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -25,10 +25,16 @@ #include #endif +#include +#include + +#include + #include #include #include #include +#include #include "module-default-device-restore-symdef.h" @@ -39,15 +45,24 @@ PA_MODULE_LOAD_ONCE(TRUE); #define DEFAULT_SINK_FILE "default-sink" #define DEFAULT_SOURCE_FILE "default-source" +#define DEFAULT_SAVE_INTERVAL 5 -int pa__init(pa_module *m) { +struct userdata { + pa_core *core; + pa_subscription *subscription; + pa_time_event *time_event; + char *sink_filename, *source_filename; + pa_bool_t modified; +}; + +static void load(struct userdata *u) { FILE *f; /* We never overwrite manually configured settings */ - if (m->core->default_sink_name) + if (u->core->default_sink_name) pa_log_info("Manually configured default sink, not overwriting."); - else if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "r"))) { + else if ((f = fopen(u->sink_filename, "r"))) { char ln[256] = ""; fgets(ln, sizeof(ln)-1, f); @@ -55,17 +70,19 @@ int pa__init(pa_module *m) { fclose(f); if (!ln[0]) - pa_log_debug("No previous default sink setting, ignoring."); - else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SINK, 1)) { - pa_namereg_set_default(m->core, ln, PA_NAMEREG_SINK); - pa_log_debug("Restored default sink '%s'.", ln); + pa_log_info("No previous default sink setting, ignoring."); + else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) { + pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK); + pa_log_info("Restored default sink '%s'.", ln); } else pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln); - } - if (m->core->default_source_name) + } else if (errno != ENOENT) + pa_log("Failed to load default sink: %s", pa_cstrerror(errno)); + + if (u->core->default_source_name) pa_log_info("Manually configured default source, not overwriting."); - else if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "r"))) { + else if ((f = fopen(u->source_filename, "r"))) { char ln[256] = ""; fgets(ln, sizeof(ln)-1, f); @@ -73,29 +90,114 @@ int pa__init(pa_module *m) { fclose(f); if (!ln[0]) - pa_log_debug("No previous default source setting, ignoring."); - else if (pa_namereg_get(m->core, ln, PA_NAMEREG_SOURCE, 1)) { - pa_namereg_set_default(m->core, ln, PA_NAMEREG_SOURCE); - pa_log_debug("Restored default source '%s'.", ln); + pa_log_info("No previous default source setting, ignoring."); + else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) { + pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE); + pa_log_info("Restored default source '%s'.", ln); } else pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln); - } - return 0; + } else if (errno != ENOENT) + pa_log("Failed to load default sink: %s", pa_cstrerror(errno)); } -void pa__done(pa_module*m) { +static void save(struct userdata *u) { FILE *f; - if ((f = pa_open_config_file(NULL, DEFAULT_SINK_FILE, NULL, NULL, "w"))) { - const char *n = pa_namereg_get_default_sink_name(m->core); - fprintf(f, "%s\n", n ? n : ""); - fclose(f); + if (!u->modified) + return; + + if (u->sink_filename) { + if ((f = fopen(u->sink_filename, "w"))) { + const char *n = pa_namereg_get_default_sink_name(u->core); + fprintf(f, "%s\n", pa_strempty(n)); + fclose(f); + } else + pa_log("Failed to save default sink: %s", pa_cstrerror(errno)); } - if ((f = pa_open_config_file(NULL, DEFAULT_SOURCE_FILE, NULL, NULL, "w"))) { - const char *n = pa_namereg_get_default_source_name(m->core); - fprintf(f, "%s\n", n ? n : ""); - fclose(f); + if (u->source_filename) { + if ((f = fopen(u->source_filename, "w"))) { + const char *n = pa_namereg_get_default_source_name(u->core); + fprintf(f, "%s\n", pa_strempty(n)); + fclose(f); + } else + pa_log("Failed to save default source: %s", pa_cstrerror(errno)); + } + + u->modified = FALSE; +} + +static void time_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) { + struct userdata *u = userdata; + + pa_assert(u); + save(u); + + if (u->time_event) { + u->core->mainloop->time_free(u->time_event); + u->time_event = NULL; + } +} + +static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + struct userdata *u = userdata; + + pa_assert(u); + + u->modified = TRUE; + + if (!u->time_event) { + struct timeval tv; + pa_gettimeofday(&tv); + pa_timeval_add(&tv, DEFAULT_SAVE_INTERVAL*PA_USEC_PER_SEC); + u->time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, time_cb, u); } } + +int pa__init(pa_module *m) { + struct userdata *u; + + pa_assert(u); + + u = pa_xnew0(struct userdata, 1); + u->core = m->core; + + if (!(u->sink_filename = pa_runtime_path(DEFAULT_SINK_FILE))) + goto fail; + + if (!(u->source_filename = pa_runtime_path(DEFAULT_SOURCE_FILE))) + goto fail; + + load(u); + + u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u); + + return 0; + +fail: + pa__done(m); + + return -1; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + save(u); + + if (u->subscription) + pa_subscription_free(u->subscription); + + if (u->time_event) + m->core->mainloop->time_free(u->time_event); + + pa_xfree(u->sink_filename); + pa_xfree(u->source_filename); + pa_xfree(u); +} diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 27c69f31..0a41b84a 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -263,7 +263,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da int pa__init(pa_module*m) { pa_modargs *ma = NULL; struct userdata *u; - char *fname, *state_dir; + char *fname, *runtime_dir; char hn[256]; pa_sink *sink; pa_source *source; @@ -290,11 +290,11 @@ int pa__init(pa_module*m) { if (!pa_get_host_name(hn, sizeof(hn))) goto fail; - if (!(state_dir = pa_get_state_dir())) + if (!(runtime_dir = pa_get_runtime_dir())) goto fail; - fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", state_dir, hn); - pa_xfree(state_dir); + fname = pa_sprintf_malloc("%s/device-volumes.%s.gdbm", runtime_dir, hn); + pa_xfree(runtime_dir); if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) { pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno)); @@ -316,6 +316,7 @@ int pa__init(pa_module*m) { fail: pa__done(m); + if (ma) pa_modargs_free(ma); diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 9a4ba58e..2206e2bc 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -143,7 +143,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { case PA_SINK_SUSPENDED: - pa_assert(PA_SINK_OPENED(u->sink->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); pa_smoother_pause(u->smoother, pa_rtclock_usec()); break; @@ -211,7 +211,7 @@ static void thread_func(void *userdata) { pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Render some data and write it to the fifo */ - if (PA_SINK_OPENED(u->sink->thread_info.state) && pollfd->revents) { + if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) { pa_usec_t usec; int64_t n; @@ -294,7 +294,7 @@ static void thread_func(void *userdata) { } /* Hmm, nothing to do. Let's sleep */ - pollfd->events = PA_SINK_OPENED(u->sink->thread_info.state) ? POLLOUT : 0; + pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0; } if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index aa398a28..6e0faac7 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -102,10 +102,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t usec = 0; + /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - *((pa_usec_t*) data) = usec /* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec) */; + /* Add the latency internal to our sink input on top */ + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); + + *((pa_usec_t*) data) = usec; return 0; } } @@ -120,7 +124,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input))) + if (PA_SINK_IS_LINKED(state) && + u->sink_input && + PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); return 0; @@ -134,7 +141,7 @@ static void sink_request_rewind(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE); } /* Called from I/O thread context */ @@ -145,24 +152,9 @@ static void sink_update_requested_latency(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s); - pa_sink_invalidate_requested_latency(u->master); -} - -/* Called from I/O thread context */ -static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { - struct userdata *u = PA_SINK_INPUT(o)->userdata; - - switch (code) { - case PA_SINK_INPUT_MESSAGE_GET_LATENCY: - *((pa_usec_t*) data) = 0 /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec)*/; - - /* Fall through, the default handler will add in the extra - * latency added by the resampler */ - break; - } - - return pa_sink_input_process_msg(o, code, data, offset, chunk); + pa_sink_input_set_requested_latency_within_thread( + u->sink_input, + pa_sink_get_requested_latency_within_thread(s)); } /* Called from I/O thread context */ @@ -192,20 +184,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk dst = (float*) pa_memblock_acquire(chunk->memblock); for (c = 0; c < u->channels; c++) { - unsigned j; - float *p, *q; - - p = src + c; - q = u->input; - for (j = 0; j < n; j++, p += u->channels, q++) - *q = PA_CLAMP_UNLIKELY(*p, -1.0, 1.0); - + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, u->channels*sizeof(float), n); u->descriptor->run(u->handle[c], n); - - q = u->output; - p = dst + c; - for (j = 0; j < n; j++, q++, p += u->channels) - *p = PA_CLAMP_UNLIKELY(*q, -1.0, 1.0); + pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels*sizeof(float), u->output, sizeof(float), n); } pa_memblock_release(tchunk.memblock); @@ -245,6 +226,9 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_assert_se(u = i->userdata); pa_sink_detach_within_thread(u->sink); + + pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); } /* Called from I/O thread context */ @@ -648,7 +632,6 @@ int pa__init(pa_module*m) { if (!u->sink_input) goto fail; - u->sink_input->parent.process_msg = sink_input_process_msg; u->sink_input->pop = sink_input_pop_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; diff --git a/src/modules/module-match.c b/src/modules/module-match.c index 0411dcdc..d0265455 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -82,12 +82,14 @@ static int load_rules(struct userdata *u, const char *filename) { pa_assert(u); - f = filename ? - fopen(fn = pa_xstrdup(filename), "r") : - pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn, "r"); + if (filename) + f = fopen(fn = pa_xstrdup(filename), "r"); + else + f = pa_open_config_file(DEFAULT_MATCH_TABLE_FILE, DEFAULT_MATCH_TABLE_FILE_USER, NULL, &fn); if (!f) { - pa_log("failed to open file '%s': %s", fn, pa_cstrerror(errno)); + pa_xfree(fn); + pa_log("Failed to open file config file: %s", pa_cstrerror(errno)); goto finish; } diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 2301f088..606b87d0 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -64,6 +64,7 @@ PA_MODULE_USAGE( "description="); #define DEFAULT_SINK_NAME "null" +#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2) struct userdata { pa_core *core; @@ -76,7 +77,8 @@ struct userdata { size_t block_size; - struct timeval timestamp; + pa_usec_t block_usec; + pa_usec_t timestamp; }; static const char* const valid_modargs[] = { @@ -96,26 +98,93 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_SET_STATE: if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING) - pa_rtclock_get(&u->timestamp); + u->timestamp = pa_rtclock_usec(); break; case PA_SINK_MESSAGE_GET_LATENCY: { - struct timeval now; + pa_usec_t now; - pa_rtclock_get(&now); + now = pa_rtclock_usec(); + *((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0; - if (pa_timeval_cmp(&u->timestamp, &now) > 0) - *((pa_usec_t*) data) = 0; - else - *((pa_usec_t*) data) = pa_timeval_diff(&u->timestamp, &now); - break; + return 0; } } return pa_sink_process_msg(o, code, data, offset, chunk); } +static void sink_update_requested_latency_cb(pa_sink *s) { + struct userdata *u; + + pa_sink_assert_ref(s); + u = s->userdata; + pa_assert(u); + + u->block_usec = pa_sink_get_requested_latency_within_thread(s); +} + +static void process_rewind(struct userdata *u, pa_usec_t now) { + size_t rewind_nbytes, in_buffer; + pa_usec_t delay; + + pa_assert(u); + + /* Figure out how much we shall rewind and reset the counter */ + rewind_nbytes = u->sink->thread_info.rewind_nbytes; + u->sink->thread_info.rewind_nbytes = 0; + + pa_assert(rewind_nbytes > 0); + pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); + + if (u->timestamp <= now) + return; + + delay = u->timestamp - now; + in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec); + + if (in_buffer <= 0) + return; + + if (rewind_nbytes > in_buffer) + rewind_nbytes = in_buffer; + + pa_sink_process_rewind(u->sink, rewind_nbytes); + u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec); + + pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); +} + +static void process_render(struct userdata *u, pa_usec_t now) { + size_t nbytes; + size_t ate = 0; + + /* This is the configured latency. Sink inputs connected to us + might not have a single frame more than this value queued. Hence: + at maximum read this many bytes from the sink inputs. */ + + nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); + + /* Fill the buffer up the the latency size */ + while (u->timestamp < now + u->block_usec) { + pa_memchunk chunk; + + pa_sink_render(u->sink, nbytes, &chunk); + pa_memblock_unref(chunk.memblock); + + pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); + u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec); + + ate += chunk.length; + + if (ate >= nbytes) + break; + } + + pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); +} + static void thread_func(void *userdata) { struct userdata *u = userdata; @@ -126,23 +195,24 @@ static void thread_func(void *userdata) { pa_thread_mq_install(&u->thread_mq); pa_rtpoll_install(u->rtpoll); - pa_rtclock_get(&u->timestamp); + u->timestamp = pa_rtclock_usec(); for (;;) { int ret; /* Render some data and drop it immediately */ if (u->sink->thread_info.state == PA_SINK_RUNNING) { - struct timeval now; + pa_usec_t now; - pa_rtclock_get(&now); + now = pa_rtclock_usec(); - if (pa_timeval_cmp(&u->timestamp, &now) <= 0) { - pa_sink_skip(u->sink, u->block_size); - pa_timeval_add(&u->timestamp, pa_bytes_to_usec(u->block_size, &u->sink->sample_spec)); - } + if (u->sink->thread_info.rewind_nbytes > 0) + process_rewind(u, now); - pa_rtpoll_set_timer_absolute(u->rtpoll, &u->timestamp); + if (u->timestamp <= now) + process_render(u, now); + + pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); } else pa_rtpoll_set_timer_disabled(u->rtpoll); @@ -197,26 +267,26 @@ int pa__init(pa_module*m) { pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "NULL sink")); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Output")); - u->sink = pa_sink_new(m->core, &data, 0); + u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); pa_sink_new_data_done(&data); if (!u->sink) { - pa_log("Failed to create sink."); + pa_log("Failed to create sink object."); goto fail; } u->sink->parent.process_msg = sink_process_msg; + u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - u->block_size = pa_bytes_per_second(&ss) / 20; /* 50 ms */ - if (u->block_size <= 0) - u->block_size = pa_frame_size(&ss); + u->block_usec = u->sink->max_latency = MAX_LATENCY_USEC; + + u->sink->thread_info.max_rewind = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); if (!(u->thread = pa_thread_new(thread_func, u))) { pa_log("Failed to create thread."); diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c index f07d82a0..3dd4508e 100644 --- a/src/modules/module-oss.c +++ b/src/modules/module-oss.c @@ -161,10 +161,10 @@ static void trigger(struct userdata *u, pa_bool_t quick) { pa_log_debug("trigger"); - if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) + if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) enable_bits |= PCM_ENABLE_INPUT; - if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) + if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) enable_bits |= PCM_ENABLE_OUTPUT; pa_log_debug("trigger: %i", enable_bits); @@ -202,7 +202,7 @@ static void trigger(struct userdata *u, pa_bool_t quick) { * register the fd as ready. */ - if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) { + if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { uint8_t *buf = pa_xnew(uint8_t, u->in_fragment_size); pa_read(u->fd, buf, u->in_fragment_size, NULL); pa_xfree(buf); @@ -641,7 +641,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { case PA_SINK_SUSPENDED: - pa_assert(PA_SINK_OPENED(u->sink->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state)); if (!u->source || u->source_suspended) { if (suspend(u) < 0) @@ -658,7 +658,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse if (u->sink->thread_info.state == PA_SINK_INIT) { do_trigger = TRUE; - quick = u->source && PA_SOURCE_OPENED(u->source->thread_info.state); + quick = u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state); } if (u->sink->thread_info.state == PA_SINK_SUSPENDED) { @@ -721,7 +721,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { case PA_SOURCE_SUSPENDED: - pa_assert(PA_SOURCE_OPENED(u->source->thread_info.state)); + pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state)); if (!u->sink || u->sink_suspended) { if (suspend(u) < 0) @@ -738,7 +738,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off if (u->source->thread_info.state == PA_SOURCE_INIT) { do_trigger = TRUE; - quick = u->sink && PA_SINK_OPENED(u->sink->thread_info.state); + quick = u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state); } if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) { @@ -877,7 +877,7 @@ static void thread_func(void *userdata) { /* Render some data and write it to the dsp */ - if (u->sink && PA_SINK_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) { + if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) { if (u->use_mmap) { @@ -985,7 +985,7 @@ static void thread_func(void *userdata) { /* Try to read some data and pass it on to the source driver. */ - if (u->source && PA_SOURCE_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) { + if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) { if (u->use_mmap) { @@ -1095,8 +1095,8 @@ static void thread_func(void *userdata) { pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->events = - ((u->source && PA_SOURCE_OPENED(u->source->thread_info.state)) ? POLLIN : 0) | - ((u->sink && PA_SINK_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0); + ((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) | + ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0); } /* Hmm, nothing to do. Let's sleep */ diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c index 600201b4..8bcc19b1 100644 --- a/src/modules/module-protocol-stub.c +++ b/src/modules/module-protocol-stub.c @@ -215,15 +215,6 @@ int pa__init(pa_module*m) { #else pa_socket_server *s; int r; - char tmp[PATH_MAX]; - -#if defined(USE_PROTOCOL_ESOUND) -#if defined(USE_PER_USER_ESOUND_SOCKET) - char esdsocketpath[PATH_MAX]; -#else - const char esdsocketpath[] = "/tmp/.esd/socket"; -#endif -#endif #endif pa_assert(m); @@ -255,27 +246,28 @@ int pa__init(pa_module*m) { goto fail; if (s_ipv4) - if (!(u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma))) - pa_socket_server_unref(s_ipv4); - + u->protocol_ipv4 = protocol_new(m->core, s_ipv4, m, ma); if (s_ipv6) - if (!(u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma))) - pa_socket_server_unref(s_ipv6); + u->protocol_ipv6 = protocol_new(m->core, s_ipv6, m, ma); if (!u->protocol_ipv4 && !u->protocol_ipv6) goto fail; + if (s_ipv6) + pa_socket_server_unref(s_ipv6); + if (s_ipv6) + pa_socket_server_unref(s_ipv4); + #else #if defined(USE_PROTOCOL_ESOUND) #if defined(USE_PER_USER_ESOUND_SOCKET) - snprintf(esdsocketpath, sizeof(esdsocketpath), "/tmp/.esd-%lu/socket", (unsigned long) getuid()); + u->socket_path = pa_sprintf_malloc("/tmp/.esd-%lu/socket", (unsigned long) getuid()); +#else + u->socket_path = pa_xstrdup("/tmp/.esd/socket"); #endif - pa_runtime_path(pa_modargs_get_value(ma, "socket", esdsocketpath), tmp, sizeof(tmp)); - u->socket_path = pa_xstrdup(tmp); - /* This socket doesn't reside in our own runtime dir but in * /tmp/.esd/, hence we have to create the dir first */ @@ -285,24 +277,26 @@ int pa__init(pa_module*m) { } #else - pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET), tmp, sizeof(tmp)); - u->socket_path = pa_xstrdup(tmp); -#endif - - if ((r = pa_unix_socket_remove_stale(tmp)) < 0) { - pa_log("Failed to remove stale UNIX socket '%s': %s", tmp, pa_cstrerror(errno)); + if (!(u->socket_path = pa_runtime_path(pa_modargs_get_value(ma, "socket", UNIX_SOCKET)))) { + pa_log("Failed to generate socket path."); goto fail; } +#endif - if (r) - pa_log("Removed stale UNIX socket '%s'.", tmp); + if ((r = pa_unix_socket_remove_stale(u->socket_path)) < 0) { + pa_log("Failed to remove stale UNIX socket '%s': %s", u->socket_path, pa_cstrerror(errno)); + goto fail; + } else if (r > 0) + pa_log_info("Removed stale UNIX socket '%s'.", u->socket_path); - if (!(s = pa_socket_server_new_unix(m->core->mainloop, tmp))) + if (!(s = pa_socket_server_new_unix(m->core->mainloop, u->socket_path))) goto fail; if (!(u->protocol_unix = protocol_new(m->core, s, m, ma))) goto fail; + pa_socket_server_unref(s); + #endif m->userdata = u; @@ -325,23 +319,21 @@ fail: #else if (u->protocol_unix) protocol_free(u->protocol_unix); - - if (u->socket_path) - pa_xfree(u->socket_path); + pa_xfree(u->socket_path); #endif pa_xfree(u); - } else { + } + #if defined(USE_TCP_SOCKETS) - if (s_ipv4) - pa_socket_server_unref(s_ipv4); - if (s_ipv6) - pa_socket_server_unref(s_ipv6); + if (s_ipv4) + pa_socket_server_unref(s_ipv4); + if (s_ipv6) + pa_socket_server_unref(s_ipv6); #else - if (s) - pa_socket_server_unref(s); + if (s) + pa_socket_server_unref(s); #endif - } goto finish; } @@ -362,7 +354,7 @@ void pa__done(pa_module*m) { if (u->protocol_unix) protocol_free(u->protocol_unix); -#if defined(USE_PROTOCOL_ESOUND) +#if defined(USE_PROTOCOL_ESOUND) && !defined(USE_PER_USER_ESOUND_SOCKET) if (u->socket_path) { char *p = pa_parent_dir(u->socket_path); rmdir(p); diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 6a16321d..f68b7191 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -81,10 +81,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t usec = 0; + /* Get the latency of the master sink */ if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) usec = 0; - *((pa_usec_t*) data) = usec/* + pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec)*/; + /* Add the latency internal to our sink input on top */ + usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec); + + *((pa_usec_t*) data) = usec; return 0; } } @@ -99,7 +103,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - if (PA_SINK_LINKED(state) && u->sink_input && PA_SINK_INPUT_LINKED(pa_sink_input_get_state(u->sink_input))) + if (PA_SINK_IS_LINKED(state) && + u->sink_input && + PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) + pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); return 0; @@ -112,7 +119,7 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE); } /* Called from I/O thread context */ @@ -123,24 +130,9 @@ static void sink_update_requested_latency(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - u->sink_input->thread_info.requested_sink_latency = pa_sink_get_requested_latency_within_thread(s); - pa_sink_invalidate_requested_latency(u->master); -} - -/* Called from I/O thread context */ -static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { - struct userdata *u = PA_SINK_INPUT(o)->userdata; - - switch (code) { - case PA_SINK_INPUT_MESSAGE_GET_LATENCY: - *((pa_usec_t*) data) = 0; /*pa_bytes_to_usec(u->memchunk.length, &u->sink_input->sample_spec);*/ - - /* Fall through, the default handler will add in the extra - * latency added by the resampler */ - break; - } - - return pa_sink_input_process_msg(o, code, data, offset, chunk); + pa_sink_input_set_requested_latency_within_thread( + u->sink_input, + pa_sink_get_requested_latency_within_thread(s)); } /* Called from I/O thread context */ @@ -152,7 +144,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert_se(u = i->userdata); pa_sink_render(u->sink, nbytes, chunk); - return 0; } @@ -185,6 +176,9 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_assert_se(u = i->userdata); pa_sink_detach_within_thread(u->sink); + + pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); } /* Called from I/O thread context */ @@ -317,7 +311,6 @@ int pa__init(pa_module*m) { if (!u->sink_input) goto fail; - u->sink_input->parent.process_msg = sink_input_process_msg; u->sink_input->pop = sink_input_pop_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index ef8239d0..a3985974 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -317,7 +317,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s if (pa_sink_used_by(s) <= 0) { - if (PA_SINK_OPENED(state)) + if (PA_SINK_IS_OPENED(state)) restart(d); } @@ -328,7 +328,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s if (pa_source_used_by(s) <= 0) { - if (PA_SOURCE_OPENED(state)) + if (PA_SOURCE_IS_OPENED(state)) restart(d); } } diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index 74d5a826..e3ae5e1f 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -303,7 +303,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse /* First, change the state, because otherwide pa_sink_render() would fail */ if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) - if (PA_SINK_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data))) + if (PA_SINK_IS_OPENED((pa_sink_state_t) PA_PTR_TO_UINT(data))) send_data(u); return r; @@ -314,7 +314,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_assert(offset > 0); u->requested_bytes += (size_t) offset; - if (PA_SINK_OPENED(u->sink->thread_info.state)) + if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) send_data(u); return 0; @@ -343,7 +343,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { switch ((pa_sink_state_t) state) { case PA_SINK_SUSPENDED: - pa_assert(PA_SINK_OPENED(s->state)); + pa_assert(PA_SINK_IS_OPENED(s->state)); stream_cork(u, TRUE); break; @@ -369,7 +369,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off switch (code) { case SOURCE_MESSAGE_POST: - if (PA_SOURCE_OPENED(u->source->thread_info.state)) + if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) pa_source_post(u->source, chunk); return 0; } @@ -385,7 +385,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) { switch ((pa_source_state_t) state) { case PA_SOURCE_SUSPENDED: - pa_assert(PA_SOURCE_OPENED(s->state)); + pa_assert(PA_SOURCE_IS_OPENED(s->state)); stream_cork(u, TRUE); break; @@ -1066,7 +1066,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t pa_tagstruct_putu32(reply, PA_INVALID_INDEX); pa_tagstruct_puts(reply, u->sink_name); pa_tagstruct_putu32(reply, u->maxlength); - pa_tagstruct_put_boolean(reply, !PA_SINK_OPENED(pa_sink_get_state(u->sink))); + pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(pa_sink_get_state(u->sink))); pa_tagstruct_putu32(reply, u->tlength); pa_tagstruct_putu32(reply, u->prebuf); pa_tagstruct_putu32(reply, u->minreq); @@ -1082,7 +1082,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t pa_tagstruct_putu32(reply, PA_INVALID_INDEX); pa_tagstruct_puts(reply, u->source_name); pa_tagstruct_putu32(reply, u->maxlength); - pa_tagstruct_put_boolean(reply, !PA_SOURCE_OPENED(pa_source_get_state(u->source))); + pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(pa_source_get_state(u->source))); pa_tagstruct_putu32(reply, u->fragsize); #endif diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c index 0dc8dcfd..336bcac9 100644 --- a/src/modules/module-volume-restore.c +++ b/src/modules/module-volume-restore.c @@ -134,16 +134,12 @@ static int load_rules(struct userdata *u) { char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256]; char *ln = buf_name; - f = u->table_file ? - fopen(u->table_file, "r") : - pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "r"); - - if (!f) { + if (!(f = fopen(u->table_file, "r"))) { if (errno == ENOENT) { - pa_log_info("starting with empty ruleset."); + pa_log_info("Starting with empty ruleset."); ret = 0; } else - pa_log("failed to open file '%s': %s", u->table_file, pa_cstrerror(errno)); + pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno)); goto finish; } @@ -236,11 +232,7 @@ static int save_rules(struct userdata *u) { pa_log_info("Saving rules..."); - f = u->table_file ? - fopen(u->table_file, "w") : - pa_open_config_file(NULL, DEFAULT_VOLUME_TABLE_FILE, NULL, &u->table_file, "w"); - - if (!f) { + if (!(f = fopen(u->table_file, "w"))) { pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno)); goto finish; } @@ -496,7 +488,7 @@ int pa__init(pa_module*m) { u = pa_xnew(struct userdata, 1); u->core = m->core; u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - u->table_file = pa_xstrdup(pa_modargs_get_value(ma, "table", NULL)); + u->table_file = pa_runtime_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE)); u->modified = FALSE; u->subscription = NULL; u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL; diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c index c054f663..75f44182 100644 --- a/src/pulse/client-conf.c +++ b/src/pulse/client-conf.c @@ -112,13 +112,20 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { table[6].data = &c->cookie_file; table[7].data = &c->disable_shm; - f = filename ? - fopen((fn = pa_xstrdup(filename)), "r") : - pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn, "r"); + if (filename) { - if (!f && errno != EINTR) { - pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); - goto finish; + if (!(f = fopen(filename, "r"))) { + pa_log("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + goto finish; + } + + fn = pa_xstrdup(fn); + + } else { + + if (!(f = pa_open_config_file(DEFAULT_CLIENT_CONFIG_FILE, DEFAULT_CLIENT_CONFIG_FILE_USER, ENV_CLIENT_CONFIG_FILE, &fn))) + if (errno != ENOENT) + goto finish; } r = f ? pa_config_parse(fn, f, table, NULL) : 0; @@ -126,7 +133,6 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) { if (!r) r = pa_client_conf_load_cookie(c); - finish: pa_xfree(fn); diff --git a/src/pulse/context.c b/src/pulse/context.c index 7806e88c..f9f021af 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio is free software; you can redistribute it and/or modify @@ -93,6 +93,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_RECORD_STREAM_MOVED] = pa_command_stream_moved, [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended, [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended, + [PA_COMMAND_STARTED] = pa_command_stream_started, [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event }; @@ -100,10 +101,12 @@ static void unlock_autospawn_lock_file(pa_context *c) { pa_assert(c); if (c->autospawn_lock_fd >= 0) { - char lf[PATH_MAX]; - pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); + char *lf; + lf = pa_runtime_path(AUTOSPAWN_LOCK); pa_unlock_lockfile(lf, c->autospawn_lock_fd); + pa_xfree(lf); + c->autospawn_lock_fd = -1; } } @@ -114,6 +117,16 @@ pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { return pa_context_new_with_proplist(mainloop, name, NULL); } +static void reset_callbacks(pa_context *c) { + pa_assert(c); + + c->state_callback = NULL; + c->state_userdata = NULL; + + c->subscribe_callback = NULL; + c->subscribe_userdata = NULL; +} + pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) { pa_context *c; @@ -146,18 +159,14 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * c->ctag = 0; c->csyncid = 0; - c->state_callback = NULL; - c->state_userdata = NULL; - - c->subscribe_callback = NULL; - c->subscribe_userdata = NULL; + reset_callbacks(c); - c->is_local = -1; + c->is_local = FALSE; c->server_list = NULL; c->server = NULL; c->autospawn_lock_fd = -1; memset(&c->spawn_api, 0, sizeof(c->spawn_api)); - c->do_autospawn = 0; + c->do_autospawn = FALSE; #ifndef MSG_NOSIGNAL #ifdef SIGPIPE @@ -186,26 +195,48 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * return c; } -static void context_free(pa_context *c) { +static void context_unlink(pa_context *c) { + pa_stream *s; + pa_assert(c); - unlock_autospawn_lock_file(c); + s = c->streams ? pa_stream_ref(c->streams) : NULL; + while (s) { + pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL; + pa_stream_set_state(s, c->state == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); + pa_stream_unref(s); + s = n; + } while (c->operations) pa_operation_cancel(c->operations); - while (c->streams) - pa_stream_set_state(c->streams, PA_STREAM_TERMINATED); - - if (c->client) - pa_socket_client_unref(c->client); - if (c->pdispatch) + if (c->pdispatch) { pa_pdispatch_unref(c->pdispatch); + c->pdispatch = NULL; + } + if (c->pstream) { pa_pstream_unlink(c->pstream); pa_pstream_unref(c->pstream); + c->pstream = NULL; } + if (c->client) { + pa_socket_client_unref(c->client); + c->client = NULL; + } + + reset_callbacks(c); +} + +static void context_free(pa_context *c) { + pa_assert(c); + + context_unlink(c); + + unlock_autospawn_lock_file(c); + if (c->record_streams) pa_dynarray_free(c->record_streams, NULL, NULL); if (c->playback_streams) @@ -252,46 +283,16 @@ void pa_context_set_state(pa_context *c, pa_context_state_t st) { pa_context_ref(c); c->state = st; + if (c->state_callback) c->state_callback(c, c->state_userdata); - if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) { - pa_stream *s; - - s = c->streams ? pa_stream_ref(c->streams) : NULL; - while (s) { - pa_stream *n = s->next ? pa_stream_ref(s->next) : NULL; - pa_stream_set_state(s, st == PA_CONTEXT_FAILED ? PA_STREAM_FAILED : PA_STREAM_TERMINATED); - pa_stream_unref(s); - s = n; - } - - if (c->pdispatch) - pa_pdispatch_unref(c->pdispatch); - c->pdispatch = NULL; - - if (c->pstream) { - pa_pstream_unlink(c->pstream); - pa_pstream_unref(c->pstream); - } - c->pstream = NULL; - - if (c->client) - pa_socket_client_unref(c->client); - c->client = NULL; - } + if (st == PA_CONTEXT_FAILED || st == PA_CONTEXT_TERMINATED) + context_unlink(c); pa_context_unref(c); } -void pa_context_fail(pa_context *c, int error) { - pa_assert(c); - pa_assert(PA_REFCNT_VALUE(c) >= 1); - - pa_context_set_error(c, error); - pa_context_set_state(c, PA_CONTEXT_FAILED); -} - int pa_context_set_error(pa_context *c, int error) { pa_assert(error >= 0); pa_assert(error < PA_ERR_MAX); @@ -302,6 +303,14 @@ int pa_context_set_error(pa_context *c, int error) { return error; } +void pa_context_fail(pa_context *c, int error) { + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_set_error(c, error); + pa_context_set_state(c, PA_CONTEXT_FAILED); +} + static void pstream_die_callback(pa_pstream *p, void *userdata) { pa_context *c = userdata; @@ -358,25 +367,41 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o pa_context_unref(c); } -int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t) { +int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail) { + uint32_t err; pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); if (command == PA_COMMAND_ERROR) { pa_assert(t); - if (pa_tagstruct_getu32(t, &c->error) < 0) { + if (pa_tagstruct_getu32(t, &err) < 0) { pa_context_fail(c, PA_ERR_PROTOCOL); return -1; - } + } else if (command == PA_COMMAND_TIMEOUT) - c->error = PA_ERR_TIMEOUT; + err = PA_ERR_TIMEOUT; else { pa_context_fail(c, PA_ERR_PROTOCOL); return -1; } + if (err == PA_OK) { + pa_context_fail(c, PA_ERR_PROTOCOL); + return -1; + } + + if (err >= PA_ERR_MAX) + err = PA_ERR_UNKNOWN; + + if (fail) { + pa_context_fail(c, err); + return -1; + } + + pa_context_set_error(c, err); + return 0; } @@ -390,11 +415,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t pa_context_ref(c); if (command != PA_COMMAND_REPLY) { - - if (pa_context_handle_error(c, command, t) < 0) - pa_context_fail(c, PA_ERR_PROTOCOL); - - pa_context_fail(c, c->error); + pa_context_handle_error(c, command, t, TRUE); goto finish; } @@ -417,7 +438,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t /* Enable shared memory support if possible */ if (c->version >= 10 && pa_mempool_is_shared(c->mempool) && - c->is_local > 0) { + c->is_local) { /* Only enable SHM if both sides are owned by the same * user. This is a security measure because otherwise @@ -486,7 +507,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) { c->pdispatch = pa_pdispatch_new(c->mainloop, command_table, PA_COMMAND_MAX); if (!c->conf->cookie_valid) - pa_log_warn("No cookie loaded. Attempting to connect without."); + pa_log_info("No cookie loaded. Attempting to connect without."); t = pa_tagstruct_command(c, PA_COMMAND_AUTH, &tag); pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION); @@ -525,10 +546,13 @@ static int context_connect_spawn(pa_context *c) { int fds[2] = { -1, -1} ; pa_iochannel *io; + if (getuid() == 0) + return -1; + pa_context_ref(c); if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { - pa_log("socketpair(): %s", pa_cstrerror(errno)); + pa_log_error("socketpair(): %s", pa_cstrerror(errno)); pa_context_fail(c, PA_ERR_INTERNAL); goto fail; } @@ -542,7 +566,7 @@ static int context_connect_spawn(pa_context *c) { c->spawn_api.prefork(); if ((pid = fork()) < 0) { - pa_log("fork(): %s", pa_cstrerror(errno)); + pa_log_error("fork(): %s", pa_cstrerror(errno)); pa_context_fail(c, PA_ERR_INTERNAL); if (c->spawn_api.postfork) @@ -557,9 +581,13 @@ static int context_connect_spawn(pa_context *c) { #define MAX_ARGS 64 const char * argv[MAX_ARGS+1]; int n; + char *f; + + pa_close_all(fds[1], -1); - /* Not required, since fds[0] has CLOEXEC enabled anyway */ - pa_assert_se(pa_close(fds[0]) == 0); + f = pa_sprintf_malloc("%i", fds[1]); + pa_set_env("PULSE_PASSED_FD", f); + pa_xfree(f); if (c->spawn_api.atfork) c->spawn_api.atfork(); @@ -592,6 +620,8 @@ static int context_connect_spawn(pa_context *c) { /* Parent */ + pa_assert_se(pa_close(fds[1]) == 0); + r = waitpid(pid, &status, 0); if (c->spawn_api.postfork) @@ -606,14 +636,12 @@ static int context_connect_spawn(pa_context *c) { goto fail; } - pa_assert_se(pa_close(fds[1]) == 0); + c->is_local = TRUE; - c->is_local = 1; + unlock_autospawn_lock_file(c); io = pa_iochannel_new(c->mainloop, fds[0], fds[0]); - setup_context(c, io); - unlock_autospawn_lock_file(c); pa_context_unref(c); @@ -665,7 +693,7 @@ static int try_next_connection(pa_context *c) { if (!(c->client = pa_socket_client_new_string(c->mainloop, u, PA_NATIVE_DEFAULT_PORT))) continue; - c->is_local = pa_socket_client_is_local(c->client); + c->is_local = !!pa_socket_client_is_local(c->client); pa_socket_client_set_callback(c->client, on_connection, c); break; } @@ -680,6 +708,7 @@ finish: static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata) { pa_context *c = userdata; + int saved_errno = errno; pa_assert(client); pa_assert(c); @@ -692,7 +721,9 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd if (!io) { /* Try the item in the list */ - if (errno == ECONNREFUSED || errno == ETIMEDOUT || errno == EHOSTUNREACH) { + if (saved_errno == ECONNREFUSED || + saved_errno == ETIMEDOUT || + saved_errno == EHOSTUNREACH) { try_next_connection(c); goto finish; } @@ -708,6 +739,25 @@ finish: pa_context_unref(c); } + +static char *get_legacy_runtime_dir(void) { + char *p, u[128]; + struct stat st; + + if (!pa_get_user_name(u, sizeof(u))) + return NULL; + + p = pa_sprintf_malloc("/tmp/pulse-%s", u); + + if (stat(p, &st) < 0) + return NULL; + + if (st.st_uid != getuid()) + return NULL; + + return p; +} + int pa_context_connect( pa_context *c, const char *server, @@ -736,8 +786,8 @@ int pa_context_connect( goto finish; } } else { - char *d; - char ufn[PATH_MAX]; + char *d, *ufn; + static char *legacy_dir; /* Prepend in reverse order */ @@ -757,25 +807,34 @@ int pa_context_connect( c->server_list = pa_strlist_prepend(c->server_list, "tcp4:localhost"); /* The system wide instance */ - c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH "/" PA_NATIVE_DEFAULT_UNIX_SOCKET); + c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET); + + /* The old per-user instance path. This is supported only to easy upgrades */ + if ((legacy_dir = get_legacy_runtime_dir())) { + char *p = pa_sprintf_malloc("%s" PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET, legacy_dir); + c->server_list = pa_strlist_prepend(c->server_list, p); + pa_xfree(p); + pa_xfree(legacy_dir); + } /* The per-user instance */ - c->server_list = pa_strlist_prepend(c->server_list, pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET, ufn, sizeof(ufn))); + c->server_list = pa_strlist_prepend(c->server_list, ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET)); + pa_xfree(ufn); /* Wrap the connection attempts in a single transaction for sane autospawn locking */ if (!(flags & PA_CONTEXT_NOAUTOSPAWN) && c->conf->autospawn) { - char lf[PATH_MAX]; + char *lf; - pa_runtime_path(AUTOSPAWN_LOCK, lf, sizeof(lf)); - pa_make_secure_parent_dir(lf, 0700, (uid_t)-1, (gid_t)-1); + lf = pa_runtime_path(AUTOSPAWN_LOCK); pa_assert(c->autospawn_lock_fd <= 0); c->autospawn_lock_fd = pa_lock_lockfile(lf); + pa_xfree(lf); if (api) c->spawn_api = *api; - c->do_autospawn = 1; - } + c->do_autospawn = TRUE; + } } pa_context_set_state(c, PA_CONTEXT_CONNECTING); @@ -791,7 +850,8 @@ void pa_context_disconnect(pa_context *c) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - pa_context_set_state(c, PA_CONTEXT_TERMINATED); + if (PA_CONTEXT_IS_GOOD(c->state)) + pa_context_set_state(c, PA_CONTEXT_TERMINATED); } pa_context_state_t pa_context_get_state(pa_context *c) { @@ -812,6 +872,9 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); + if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED) + return; + c->state_callback = cb; c->state_userdata = userdata; } @@ -820,11 +883,7 @@ int pa_context_is_pending(pa_context *c) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - PA_CHECK_VALIDITY(c, - c->state == PA_CONTEXT_CONNECTING || - c->state == PA_CONTEXT_AUTHORIZING || - c->state == PA_CONTEXT_SETTING_NAME || - c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE); return (c->pstream && pa_pstream_is_pending(c->pstream)) || (c->pdispatch && pa_pdispatch_is_pending(c->pdispatch)) || @@ -901,7 +960,7 @@ void pa_context_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_U goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; success = 0; @@ -920,7 +979,7 @@ finish: pa_operation_unref(o); } -pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) { +pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) { pa_tagstruct *t; pa_operation *o; uint32_t tag; @@ -930,32 +989,20 @@ pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, 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); + o = pa_operation_new(c, NULL, cb, userdata); - t = pa_tagstruct_command(c, PA_COMMAND_EXIT, &tag); + t = pa_tagstruct_command(c, command, &tag); 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, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); return o; } -pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, pa_pdispatch_cb_t internal_cb, pa_operation_cb_t cb, void *userdata) { - pa_tagstruct *t; - pa_operation *o; - uint32_t tag; - +pa_operation* pa_context_exit_daemon(pa_context *c, pa_context_success_cb_t cb, void *userdata) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); - - o = pa_operation_new(c, NULL, cb, userdata); - - t = pa_tagstruct_command(c, command, &tag); - pa_pstream_send_tagstruct(c->pstream, t); - pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, internal_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); - - return o; + return pa_context_send_simple_command(c, PA_COMMAND_EXIT, pa_context_simple_ack_callback, (pa_operation_cb_t) cb, userdata); } pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { @@ -969,7 +1016,6 @@ pa_operation* pa_context_set_default_sink(pa_context *c, const char *name, pa_co 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); - t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SINK, &tag); pa_tagstruct_puts(t, name); pa_pstream_send_tagstruct(c->pstream, t); @@ -989,7 +1035,6 @@ pa_operation* pa_context_set_default_source(pa_context *c, const char *name, pa_ 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); - t = pa_tagstruct_command(c, PA_COMMAND_SET_DEFAULT_SOURCE, &tag); pa_tagstruct_puts(t, name); pa_pstream_send_tagstruct(c->pstream, t); @@ -1002,15 +1047,13 @@ int pa_context_is_local(pa_context *c) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); - PA_CHECK_VALIDITY(c, c->is_local >= 0, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, -1); - return c->is_local; + return !!c->is_local; } pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_success_cb_t cb, void *userdata) { - pa_tagstruct *t; pa_operation *o; - uint32_t tag; pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); @@ -1020,11 +1063,14 @@ pa_operation* pa_context_set_name(pa_context *c, const char *name, pa_context_su 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); - } else { + pa_tagstruct *t; + uint32_t tag; + 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); @@ -1062,7 +1108,7 @@ 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); + PA_CHECK_VALIDITY_RETURN_ANY(c, PA_CONTEXT_IS_GOOD(c->state), PA_ERR_BADSTATE, PA_INVALID_INDEX); return c->version; } diff --git a/src/pulse/def.h b/src/pulse/def.h index 8a83d7a9..1a0b9cb6 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -48,6 +48,15 @@ typedef enum pa_context_state { PA_CONTEXT_TERMINATED /**< The connection was terminated cleanly */ } pa_context_state_t; +/** Return non-zero if the passed state is one of the connected states */ +static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) { + return + x == PA_CONTEXT_CONNECTING || + x == PA_CONTEXT_AUTHORIZING || + x == PA_CONTEXT_SETTING_NAME || + x == PA_CONTEXT_READY; +} + /** The state of a stream */ typedef enum pa_stream_state { PA_STREAM_UNCONNECTED, /**< The stream is not yet connected to any sink or source */ @@ -57,6 +66,13 @@ typedef enum pa_stream_state { PA_STREAM_TERMINATED /**< The stream has been terminated cleanly */ } pa_stream_state_t; +/** Return non-zero if the passed state is one of the connected states */ +static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) { + return + x == PA_STREAM_CREATING || + x == PA_STREAM_READY; +} + /** The state of an operation */ typedef enum pa_operation_state { PA_OPERATION_RUNNING, /**< The operation is still running */ @@ -296,6 +312,7 @@ enum { PA_ERR_VERSION, /**< Incompatible protocol version */ PA_ERR_TOOLARGE, /**< Data too large */ PA_ERR_NOTSUPPORTED, /**< Operation not supported \since 0.9.5 */ + PA_ERR_UNKNOWN, /**< The error code was unknown to the client */ PA_ERR_MAX /**< Not really an error but the first invalid error code */ }; @@ -368,7 +385,15 @@ typedef struct pa_timing_info { pa_usec_t source_usec; /**< Time in usecs a sample takes from being recorded to being delivered to the application. Only for record streams. */ pa_usec_t transport_usec; /**< Estimated time in usecs a sample takes to be transferred to/from the daemon. For both playback and record streams. */ - int playing; /**< Non-zero when the stream is currently playing. Only for playback streams. */ + int playing; /**< Non-zero when the stream is + * currently not underrun and data is + * being passed on to the device. Only + * for playback streams. This field does + * not say whether the data is actually + * already being played. To determine + * this check whether since_underrun + * (converted to usec) is larger than + * sink_usec.*/ int write_index_corrupt; /**< Non-zero if write_index is not * up-to-date because a local write @@ -403,6 +428,14 @@ typedef struct pa_timing_info { * the sink. \since 0.9.11 */ pa_usec_t configured_source_usec; /**< The static configured latency for * the source. \since 0.9.11 */ + + int64_t since_underrun; /**< Bytes that were handed to the sink + since the last underrun happened, or + since playback started again after + the last underrun. playing will tell + you which case it is. \since + 0.9.11 */ + } pa_timing_info; /** A structure for the spawn api. This may be used to integrate auto diff --git a/src/pulse/internal.h b/src/pulse/internal.h index f15c69c3..d346e945 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -42,6 +42,7 @@ #include #include #include +#include #include "client-conf.h" @@ -69,14 +70,13 @@ struct pa_context { pa_context_notify_cb_t state_callback; void *state_userdata; - pa_context_subscribe_cb_t subscribe_callback; void *subscribe_userdata; pa_mempool *mempool; - int is_local; - int do_autospawn; + pa_bool_t is_local; + pa_bool_t do_autospawn; int autospawn_lock_fd; pa_spawn_api spawn_api; @@ -89,35 +89,39 @@ struct pa_context { uint32_t client_index; }; -#define PA_MAX_WRITE_INDEX_CORRECTIONS 10 +#define PA_MAX_WRITE_INDEX_CORRECTIONS 32 typedef struct pa_index_correction { uint32_t tag; - int valid; int64_t value; - int absolute, corrupt; + pa_bool_t valid:1; + pa_bool_t absolute:1; + pa_bool_t corrupt:1; } pa_index_correction; struct pa_stream { PA_REFCNT_DECLARE; + PA_LLIST_FIELDS(pa_stream); + pa_context *context; pa_mainloop_api *mainloop; - PA_LLIST_FIELDS(pa_stream); - pa_proplist *proplist; - pa_bool_t manual_buffer_attr; - pa_buffer_attr buffer_attr; + pa_stream_direction_t direction; + pa_stream_state_t state; + pa_stream_flags_t flags; + pa_sample_spec sample_spec; pa_channel_map channel_map; - pa_stream_flags_t flags; + + pa_proplist *proplist; + uint32_t channel; + pa_bool_t channel_valid; uint32_t syncid; - int channel_valid; uint32_t stream_index; - pa_stream_direction_t direction; - pa_stream_state_t state; uint32_t requested_bytes; + pa_buffer_attr buffer_attr; uint32_t device_index; char *device_name; @@ -127,11 +131,11 @@ struct pa_stream { void *peek_data; pa_memblockq *record_memblockq; - int corked; + pa_bool_t corked; /* Store latest latency info */ pa_timing_info timing_info; - int timing_info_valid; + pa_bool_t timing_info_valid; /* Use to make sure that time advances monotonically */ pa_usec_t previous_time; @@ -146,10 +150,9 @@ struct pa_stream { /* Latency interpolation stuff */ pa_time_event *auto_timing_update_event; - int auto_timing_update_requested; + pa_bool_t auto_timing_update_requested; - pa_usec_t cached_time; - int cached_time_valid; + pa_smoother *smoother; /* Callbacks */ pa_stream_notify_cb_t state_callback; @@ -168,6 +171,8 @@ struct pa_stream { void *moved_userdata; pa_stream_notify_cb_t suspended_callback; void *suspended_userdata; + pa_stream_notify_cb_t started_callback; + void *started_userdata; }; typedef void (*pa_operation_cb_t)(void); @@ -193,7 +198,7 @@ void pa_command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); 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); 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); @@ -205,7 +210,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t void pa_context_fail(pa_context *c, int error); int pa_context_set_error(pa_context *c, int error); void pa_context_set_state(pa_context *c, pa_context_state_t st); -int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t); +int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa_bool_t fail); pa_operation* pa_context_send_simple_command(pa_context *c, uint32_t command, void (*internal_callback)(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata), void (*cb)(void), void *userdata); void pa_stream_set_state(pa_stream *s, pa_stream_state_t st); diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 49f93463..857e82b4 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -52,7 +52,7 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNU goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; p = NULL; @@ -95,7 +95,7 @@ static void context_get_server_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; p = NULL; @@ -140,7 +140,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, P goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; eol = -1; @@ -261,7 +261,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; eol = -1; @@ -382,7 +382,7 @@ static void context_get_client_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; eol = -1; @@ -464,7 +464,7 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; eol = -1; @@ -543,7 +543,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; eol = -1; @@ -637,7 +637,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; eol = -1; @@ -967,7 +967,7 @@ static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; eol = -1; @@ -1111,7 +1111,7 @@ static void context_index_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; idx = PA_INVALID_INDEX; @@ -1172,7 +1172,7 @@ static void context_get_autoload_info_callback(pa_pdispatch *pd, uint32_t comman goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; eol = -1; diff --git a/src/pulse/scache.c b/src/pulse/scache.c index 24f340ea..e43a0b9f 100644 --- a/src/pulse/scache.c +++ b/src/pulse/scache.c @@ -108,7 +108,7 @@ static void play_sample_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_ goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; success = 0; @@ -141,7 +141,7 @@ static void play_sample_with_proplist_ack_callback(pa_pdispatch *pd, uint32_t co goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; idx = PA_INVALID_INDEX; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index ccbabb55..297e9d7f 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -38,16 +38,47 @@ #include #include #include +#include #include "internal.h" -#define LATENCY_IPOL_INTERVAL_USEC (100000L) +#define LATENCY_IPOL_INTERVAL_USEC (500*PA_USEC_PER_MSEC) + +#define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC) +#define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC) 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) { +static void reset_callbacks(pa_stream *s) { + s->read_callback = NULL; + s->read_userdata = NULL; + s->write_callback = NULL; + s->write_userdata = NULL; + s->state_callback = NULL; + s->state_userdata = NULL; + s->overflow_callback = NULL; + s->overflow_userdata = NULL; + s->underflow_callback = NULL; + s->underflow_userdata = NULL; + s->latency_update_callback = NULL; + s->latency_update_userdata = NULL; + s->moved_callback = NULL; + s->moved_userdata = NULL; + s->suspended_callback = NULL; + s->suspended_userdata = NULL; + s->started_callback = NULL; + s->started_userdata = 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; @@ -58,7 +89,7 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa 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); + PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && 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); @@ -68,70 +99,53 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa s->context = c; s->mainloop = c->mainloop; - s->read_callback = NULL; - s->read_userdata = NULL; - s->write_callback = NULL; - s->write_userdata = NULL; - s->state_callback = NULL; - s->state_userdata = NULL; - s->overflow_callback = NULL; - s->overflow_userdata = NULL; - s->underflow_callback = NULL; - s->underflow_userdata = NULL; - s->latency_update_callback = NULL; - s->latency_update_userdata = NULL; - s->moved_callback = NULL; - s->moved_userdata = NULL; - s->suspended_callback = NULL; - s->suspended_userdata = NULL; - s->direction = PA_STREAM_NODIRECTION; + s->state = PA_STREAM_UNCONNECTED; + s->flags = 0; + 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(s->proplist, PA_PROP_MEDIA_NAME, name); s->channel = 0; - s->channel_valid = 0; + s->channel_valid = FALSE; s->syncid = c->csyncid++; s->stream_index = PA_INVALID_INDEX; - s->requested_bytes = 0; - s->state = PA_STREAM_UNCONNECTED; - s->manual_buffer_attr = FALSE; + s->requested_bytes = 0; memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); s->device_index = PA_INVALID_INDEX; s->device_name = NULL; s->suspended = FALSE; - s->peek_memchunk.index = 0; - s->peek_memchunk.length = 0; - s->peek_memchunk.memblock = NULL; + pa_memchunk_reset(&s->peek_memchunk); s->peek_data = NULL; s->record_memblockq = NULL; - s->previous_time = 0; + s->corked = FALSE; + memset(&s->timing_info, 0, sizeof(s->timing_info)); s->timing_info_valid = FALSE; + + s->previous_time = 0; + s->read_index_not_before = 0; s->write_index_not_before = 0; - for (i = 0; i < PA_MAX_WRITE_INDEX_CORRECTIONS; i++) s->write_index_corrections[i].valid = 0; s->current_write_index_correction = 0; - s->corked = 0; + s->auto_timing_update_event = NULL; + s->auto_timing_update_requested = FALSE; - s->cached_time_valid = 0; + reset_callbacks(s); - s->auto_timing_update_event = NULL; - s->auto_timing_update_requested = 0; + s->smoother = NULL; /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */ PA_LLIST_PREPEND(pa_stream, c->streams, s); @@ -140,16 +154,51 @@ pa_stream *pa_stream_new_with_proplist(pa_context *c, const char *name, const pa return s; } -static void stream_free(pa_stream *s) { +static void stream_unlink(pa_stream *s) { + pa_operation *o, *n; pa_assert(s); - pa_assert(!s->context); - pa_assert(!s->channel_valid); + + if (!s->context) + return; + + /* Detach from context */ + + /* Unref all operatio object that point to us */ + for (o = s->context->operations; o; o = n) { + n = o->next; + + if (o->stream == s) + pa_operation_cancel(o); + } + + /* Drop all outstanding replies for this stream */ + if (s->context->pdispatch) + pa_pdispatch_unregister_reply(s->context->pdispatch, s); + + if (s->channel_valid) { + pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL); + s->channel = 0; + s->channel_valid = FALSE; + } + + PA_LLIST_REMOVE(pa_stream, s->context->streams, s); + pa_stream_unref(s); + + s->context = NULL; if (s->auto_timing_update_event) { pa_assert(s->mainloop); s->mainloop->time_free(s->auto_timing_update_event); } + reset_callbacks(s); +} + +static void stream_free(pa_stream *s) { + pa_assert(s); + + stream_unlink(s); + if (s->peek_memchunk.memblock) { if (s->peek_data) pa_memblock_release(s->peek_memchunk.memblock); @@ -162,6 +211,9 @@ static void stream_free(pa_stream *s) { if (s->proplist) pa_proplist_free(s->proplist); + if (s->smoother) + pa_smoother_free(s->smoother); + pa_xfree(s->device_name); pa_xfree(s); } @@ -215,46 +267,41 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st) { pa_stream_ref(s); s->state = st; + if (s->state_callback) s->state_callback(s, s->state_userdata); - if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED) && s->context) { - - /* Detach from context */ - pa_operation *o, *n; + if ((st == PA_STREAM_FAILED || st == PA_STREAM_TERMINATED)) + stream_unlink(s); - /* Unref all operatio object that point to us */ - for (o = s->context->operations; o; o = n) { - n = o->next; - - if (o->stream == s) - pa_operation_cancel(o); - } - - /* Drop all outstanding replies for this stream */ - if (s->context->pdispatch) - pa_pdispatch_unregister_reply(s->context->pdispatch, s); + pa_stream_unref(s); +} - if (s->channel_valid) - pa_dynarray_put((s->direction == PA_STREAM_PLAYBACK) ? s->context->playback_streams : s->context->record_streams, s->channel, NULL); +static void request_auto_timing_update(pa_stream *s, pa_bool_t force) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); - PA_LLIST_REMOVE(pa_stream, s->context->streams, s); - pa_stream_unref(s); + if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE)) + return; - s->channel = 0; - s->channel_valid = 0; + if (s->state == PA_STREAM_READY && + (force || !s->auto_timing_update_requested)) { + pa_operation *o; - s->context = NULL; +/* pa_log("automatically requesting new timing data"); */ - s->read_callback = NULL; - s->write_callback = NULL; - s->state_callback = NULL; - s->overflow_callback = NULL; - s->underflow_callback = NULL; - s->latency_update_callback = NULL; + if ((o = pa_stream_update_timing_info(s, NULL, NULL))) { + pa_operation_unref(o); + s->auto_timing_update_requested = TRUE; + } } - pa_stream_unref(s); + if (s->auto_timing_update_event) { + struct timeval next; + pa_gettimeofday(&next); + pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC); + s->mainloop->time_restart(s->auto_timing_update_event, &next); + } } void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -279,6 +326,9 @@ void pa_command_stream_killed(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_KILLED ? c->playback_streams : c->record_streams, channel))) goto finish; + if (s->state != PA_STREAM_READY) + goto finish; + pa_context_set_error(c, PA_ERR_KILLED); pa_stream_set_state(s, PA_STREAM_FAILED); @@ -293,6 +343,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u const char *dn; pa_bool_t suspended; uint32_t di; + pa_usec_t usec; pa_assert(pd); pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED); @@ -310,12 +361,23 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u if (pa_tagstruct_getu32(t, &channel) < 0 || pa_tagstruct_getu32(t, &di) < 0 || pa_tagstruct_gets(t, &dn) < 0 || - pa_tagstruct_get_boolean(t, &suspended) < 0 || - !pa_tagstruct_eof(t)) { + pa_tagstruct_get_boolean(t, &suspended) < 0) { pa_context_fail(c, PA_ERR_PROTOCOL); goto finish; } + if (c->version >= 13) { + if (pa_tagstruct_get_usec(t, &usec) < 0) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + } + + if (!pa_tagstruct_eof(t)) { + pa_context_fail(s->context, PA_ERR_PROTOCOL); + goto finish; + } + if (!dn || di == PA_INVALID_INDEX) { pa_context_fail(c, PA_ERR_PROTOCOL); goto finish; @@ -324,12 +386,24 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_MOVED ? c->playback_streams : c->record_streams, channel))) goto finish; + if (s->state != PA_STREAM_READY) + goto finish; + + if (c->version >= 13) { + if (s->direction == PA_STREAM_RECORD) + s->timing_info.configured_source_usec = usec; + else + s->timing_info.configured_sink_usec = usec; + } + pa_xfree(s->device_name); s->device_name = pa_xstrdup(dn); s->device_index = di; s->suspended = suspended; + request_auto_timing_update(s, TRUE); + if (s->moved_callback) s->moved_callback(s, s->moved_userdata); @@ -366,8 +440,23 @@ void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUS if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_SUSPENDED ? c->playback_streams : c->record_streams, channel))) goto finish; + if (s->state != PA_STREAM_READY) + goto finish; + s->suspended = suspended; + if (s->smoother) { + pa_usec_t x = pa_rtclock_usec(); + + if (s->timing_info_valid) + x -= s->timing_info.transport_usec; + + if (s->suspended || s->corked) + pa_smoother_pause(s->smoother, x); + } + + request_auto_timing_update(s, TRUE); + if (s->suspended_callback) s->suspended_callback(s, s->suspended_userdata); @@ -375,6 +464,45 @@ finish: pa_context_unref(c); } +void pa_command_stream_started(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_assert(pd); + pa_assert(command == PA_COMMAND_STARTED); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_ref(c); + + if (c->version < 13) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (pa_tagstruct_getu32(t, &channel) < 0 || + !pa_tagstruct_eof(t)) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(c->playback_streams, channel))) + goto finish; + + if (s->state != PA_STREAM_READY) + goto finish; + + request_auto_timing_update(s, TRUE); + + if (s->started_callback) + s->started_callback(s, s->suspended_userdata); + +finish: + pa_context_unref(c); +} + void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_stream *s; pa_context *c = userdata; @@ -398,12 +526,13 @@ void pa_command_request(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32 if (!(s = pa_dynarray_get(c->playback_streams, channel))) goto finish; - if (s->state == PA_STREAM_READY) { - s->requested_bytes += bytes; + if (s->state != PA_STREAM_READY) + goto finish; - if (s->requested_bytes > 0 && s->write_callback) - s->write_callback(s, s->requested_bytes, s->write_userdata); - } + s->requested_bytes += bytes; + + if (s->requested_bytes > 0 && s->write_callback) + s->write_callback(s, s->requested_bytes, s->write_userdata); finish: pa_context_unref(c); @@ -431,6 +560,21 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC if (!(s = pa_dynarray_get(c->playback_streams, channel))) goto finish; + if (s->state != PA_STREAM_READY) + goto finish; + + if (s->smoother) + if (s->direction == PA_STREAM_PLAYBACK && s->buffer_attr.prebuf > 0) { + pa_usec_t x = pa_rtclock_usec(); + + if (s->timing_info_valid) + x -= s->timing_info.transport_usec; + + pa_smoother_pause(s->smoother, x); + } + + request_auto_timing_update(s, TRUE); + if (s->state == PA_STREAM_READY) { if (command == PA_COMMAND_OVERFLOW) { @@ -446,34 +590,7 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, PA_GCC pa_context_unref(c); } -static void request_auto_timing_update(pa_stream *s, int force) { - pa_assert(s); - pa_assert(PA_REFCNT_VALUE(s) >= 1); - - if (!(s->flags & PA_STREAM_AUTO_TIMING_UPDATE)) - return; - - if (s->state == PA_STREAM_READY && - (force || !s->auto_timing_update_requested)) { - pa_operation *o; - -/* pa_log("automatically requesting new timing data"); */ - - if ((o = pa_stream_update_timing_info(s, NULL, NULL))) { - pa_operation_unref(o); - s->auto_timing_update_requested = TRUE; - } - } - - if (s->auto_timing_update_event) { - struct timeval next; - pa_gettimeofday(&next); - pa_timeval_add(&next, LATENCY_IPOL_INTERVAL_USEC); - s->mainloop->time_restart(s->auto_timing_update_event, &next); - } -} - -static void invalidate_indexes(pa_stream *s, int r, int w) { +static void invalidate_indexes(pa_stream *s, pa_bool_t r, pa_bool_t w) { pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); @@ -500,11 +617,7 @@ static void invalidate_indexes(pa_stream *s, int r, int w) { /* pa_log("read_index invalidated"); */ } - if ((s->direction == PA_STREAM_PLAYBACK && r) || - (s->direction == PA_STREAM_RECORD && w)) - s->cached_time_valid = 0; - - request_auto_timing_update(s, 1); + request_auto_timing_update(s, TRUE); } static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC_UNUSED pa_time_event *e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) { @@ -513,10 +626,8 @@ static void auto_timing_update_callback(PA_GCC_UNUSED pa_mainloop_api *m, PA_GCC pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); -/* pa_log("time event"); */ - pa_stream_ref(s); - request_auto_timing_update(s, 0); + request_auto_timing_update(s, FALSE); pa_stream_unref(s); } @@ -536,6 +647,8 @@ static void create_stream_complete(pa_stream *s) { tv.tv_usec += LATENCY_IPOL_INTERVAL_USEC; /* every 100 ms */ pa_assert(!s->auto_timing_update_event); s->auto_timing_update_event = s->mainloop->time_new(s->mainloop, &tv, &auto_timing_update_callback, s); + + request_auto_timing_update(s, TRUE); } } @@ -577,7 +690,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED pa_stream_ref(s); if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(s->context, command, t) < 0) + if (pa_context_handle_error(s->context, command, t, FALSE) < 0) goto finish; pa_stream_set_state(s, PA_STREAM_FAILED); @@ -585,7 +698,8 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED } if (pa_tagstruct_getu32(t, &s->channel) < 0 || - ((s->direction != PA_STREAM_UPLOAD) && pa_tagstruct_getu32(t, &s->stream_index) < 0) || + s->channel == PA_INVALID_INDEX || + ((s->direction != PA_STREAM_UPLOAD) && (pa_tagstruct_getu32(t, &s->stream_index) < 0 || s->stream_index == PA_INVALID_INDEX)) || ((s->direction != PA_STREAM_RECORD) && pa_tagstruct_getu32(t, &s->requested_bytes) < 0)) { pa_context_fail(s->context, PA_ERR_PROTOCOL); goto finish; @@ -676,7 +790,7 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED NULL); } - s->channel_valid = 1; + s->channel_valid = TRUE; pa_dynarray_put((s->direction == PA_STREAM_RECORD) ? s->context->record_streams : s->context->playback_streams, s->channel, s); create_stream_complete(s); @@ -737,16 +851,23 @@ static int create_stream( if (sync_stream) s->syncid = sync_stream->syncid; - if (attr) { + if (attr) s->buffer_attr = *attr; - s->manual_buffer_attr = TRUE; - } else { - memset(&s->buffer_attr, 0, sizeof(s->buffer_attr)); - s->manual_buffer_attr = FALSE; - } - automatic_buffer_attr(s, &s->buffer_attr, &s->sample_spec); + if (flags & PA_STREAM_INTERPOLATE_TIMING) { + pa_usec_t x; + + if (s->smoother) + pa_smoother_free(s->smoother); + + s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS)); + + x = pa_rtclock_usec(); + pa_smoother_set_time_offset(s->smoother, x); + pa_smoother_pause(s->smoother, x); + } + if (!dev) dev = s->direction == PA_STREAM_PLAYBACK ? s->context->conf->default_sink : s->context->conf->default_source; @@ -922,31 +1043,31 @@ int pa_stream_write( if (s->write_index_corrections[s->current_write_index_correction].valid) { if (seek == PA_SEEK_ABSOLUTE) { - s->write_index_corrections[s->current_write_index_correction].corrupt = 0; - s->write_index_corrections[s->current_write_index_correction].absolute = 1; + s->write_index_corrections[s->current_write_index_correction].corrupt = FALSE; + s->write_index_corrections[s->current_write_index_correction].absolute = TRUE; s->write_index_corrections[s->current_write_index_correction].value = offset + length; } else if (seek == PA_SEEK_RELATIVE) { if (!s->write_index_corrections[s->current_write_index_correction].corrupt) s->write_index_corrections[s->current_write_index_correction].value += offset + length; } else - s->write_index_corrections[s->current_write_index_correction].corrupt = 1; + s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE; } /* Update the write index in the already available latency data */ if (s->timing_info_valid) { if (seek == PA_SEEK_ABSOLUTE) { - s->timing_info.write_index_corrupt = 0; + s->timing_info.write_index_corrupt = FALSE; s->timing_info.write_index = offset + length; } else if (seek == PA_SEEK_RELATIVE) { if (!s->timing_info.write_index_corrupt) s->timing_info.write_index += offset + length; } else - s->timing_info.write_index_corrupt = 1; + s->timing_info.write_index_corrupt = TRUE; } if (!s->timing_info_valid || s->timing_info.write_index_corrupt) - request_auto_timing_update(s, 1); + request_auto_timing_update(s, TRUE); } return 0; @@ -995,9 +1116,7 @@ int pa_stream_drop(pa_stream *s) { pa_assert(s->peek_data); pa_memblock_release(s->peek_memchunk.memblock); pa_memblock_unref(s->peek_memchunk.memblock); - s->peek_memchunk.length = 0; - s->peek_memchunk.index = 0; - s->peek_memchunk.memblock = NULL; + pa_memchunk_reset(&s->peek_memchunk); return 0; } @@ -1043,11 +1162,71 @@ pa_operation * pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *us return o; } +static pa_usec_t calc_time(pa_stream *s, pa_bool_t ignore_transport) { + pa_usec_t usec; + + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + pa_assert(s->state == PA_STREAM_READY); + pa_assert(s->direction != PA_STREAM_UPLOAD); + pa_assert(s->timing_info_valid); + pa_assert(s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt); + pa_assert(s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt); + + if (s->direction == PA_STREAM_PLAYBACK) { + /* The last byte that was written into the output device + * had this time value associated */ + usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec); + + if (!s->corked && !s->suspended) { + + if (!ignore_transport) + /* Because the latency info took a little time to come + * to us, we assume that the real output time is actually + * a little ahead */ + usec += s->timing_info.transport_usec; + + /* However, the output device usually maintains a buffer + too, hence the real sample currently played is a little + back */ + if (s->timing_info.sink_usec >= usec) + usec = 0; + else + usec -= s->timing_info.sink_usec; + } + + } else if (s->direction == PA_STREAM_RECORD) { + /* The last byte written into the server side queue had + * this time value associated */ + usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec); + + if (!s->corked && !s->suspended) { + + if (!ignore_transport) + /* Add transport latency */ + usec += s->timing_info.transport_usec; + + /* Add latency of data in device buffer */ + usec += s->timing_info.source_usec; + + /* If this is a monitor source, we need to correct the + * time by the playback device buffer */ + if (s->timing_info.sink_usec >= usec) + usec = 0; + else + usec -= s->timing_info.sink_usec; + } + } + + return usec; +} + static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; struct timeval local, remote, now; pa_timing_info *i; pa_bool_t playing = FALSE; + uint64_t underrun_for = 0, playing_for = 0; pa_assert(pd); pa_assert(o); @@ -1061,29 +1240,46 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, /* pa_log("pre corrupt w:%u r:%u\n", !o->stream->timing_info_valid || i->write_index_corrupt,!o->stream->timing_info_valid || i->read_index_corrupt); */ o->stream->timing_info_valid = FALSE; - i->write_index_corrupt = 0; - i->read_index_corrupt = 0; + i->write_index_corrupt = FALSE; + i->read_index_corrupt = FALSE; /* pa_log("timing update %u\n", tag); */ if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; - } else if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 || - pa_tagstruct_get_usec(t, &i->source_usec) < 0 || - pa_tagstruct_get_boolean(t, &playing) < 0 || - pa_tagstruct_get_timeval(t, &local) < 0 || - pa_tagstruct_get_timeval(t, &remote) < 0 || - pa_tagstruct_gets64(t, &i->write_index) < 0 || - pa_tagstruct_gets64(t, &i->read_index) < 0 || - !pa_tagstruct_eof(t)) { - pa_context_fail(o->context, PA_ERR_PROTOCOL); - goto finish; - } else { - o->stream->timing_info_valid = 1; + + if (pa_tagstruct_get_usec(t, &i->sink_usec) < 0 || + pa_tagstruct_get_usec(t, &i->source_usec) < 0 || + pa_tagstruct_get_boolean(t, &playing) < 0 || + pa_tagstruct_get_timeval(t, &local) < 0 || + pa_tagstruct_get_timeval(t, &remote) < 0 || + pa_tagstruct_gets64(t, &i->write_index) < 0 || + pa_tagstruct_gets64(t, &i->read_index) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + if (o->context->version >= 13) + if (pa_tagstruct_getu64(t, &underrun_for) < 0 || + pa_tagstruct_getu64(t, &playing_for) < 0) { + + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + + if (!pa_tagstruct_eof(t)) { + pa_context_fail(o->context, PA_ERR_PROTOCOL); + goto finish; + } + + o->stream->timing_info_valid = TRUE; i->playing = (int) playing; + i->since_underrun = playing ? playing_for : underrun_for; pa_gettimeofday(&now); @@ -1096,22 +1292,22 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, else i->transport_usec = pa_timeval_diff(&now, &remote); - i->synchronized_clocks = 1; + i->synchronized_clocks = TRUE; i->timestamp = remote; } else { /* clocks are not synchronized, let's estimate latency then */ i->transport_usec = pa_timeval_diff(&now, &local)/2; - i->synchronized_clocks = 0; + i->synchronized_clocks = FALSE; i->timestamp = local; pa_timeval_add(&i->timestamp, i->transport_usec); } /* Invalidate read and write indexes if necessary */ if (tag < o->stream->read_index_not_before) - i->read_index_corrupt = 1; + i->read_index_corrupt = TRUE; if (tag < o->stream->write_index_not_before) - i->write_index_corrupt = 1; + i->write_index_corrupt = TRUE; if (o->stream->direction == PA_STREAM_PLAYBACK) { /* Write index correction */ @@ -1137,11 +1333,11 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, if (o->stream->write_index_corrections[j].corrupt) { /* A corrupting seek was made */ i->write_index = 0; - i->write_index_corrupt = 1; + i->write_index_corrupt = TRUE; } else if (o->stream->write_index_corrections[j].absolute) { /* An absolute seek was made */ i->write_index = o->stream->write_index_corrections[j].value; - i->write_index_corrupt = 0; + i->write_index_corrupt = FALSE; } else if (!i->write_index_corrupt) { /* A relative seek was made */ i->write_index += o->stream->write_index_corrections[j].value; @@ -1156,25 +1352,57 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, i->read_index -= pa_memblockq_get_length(o->stream->record_memblockq); } - o->stream->cached_time_valid = 0; - } - - o->stream->auto_timing_update_requested = 0; /* pa_log("post corrupt w:%u r:%u\n", i->write_index_corrupt || !o->stream->timing_info_valid, i->read_index_corrupt || !o->stream->timing_info_valid); */ - /* Clear old correction entries */ - if (o->stream->direction == PA_STREAM_PLAYBACK) { - int n; + /* Clear old correction entries */ + if (o->stream->direction == PA_STREAM_PLAYBACK) { + int n; + + for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) { + if (!o->stream->write_index_corrections[n].valid) + continue; + + if (o->stream->write_index_corrections[n].tag <= tag) + o->stream->write_index_corrections[n].valid = FALSE; + } + } + + /* Update smoother */ + if (o->stream->smoother) { + pa_usec_t u, x; + + u = x = pa_rtclock_usec() - i->transport_usec; - for (n = 0; n < PA_MAX_WRITE_INDEX_CORRECTIONS; n++) { - if (!o->stream->write_index_corrections[n].valid) - continue; + if (o->stream->direction == PA_STREAM_PLAYBACK && + o->context->version >= 13) { + pa_usec_t su; - if (o->stream->write_index_corrections[n].tag <= tag) - o->stream->write_index_corrections[n].valid = 0; + /* If we weren't playing then it will take some time + * until the audio will actually come out through the + * speakers. Since we follow that timing here, we need + * to try to fix this up */ + + su = pa_bytes_to_usec(i->since_underrun, &o->stream->sample_spec); + + if (su < i->sink_usec) + x += i->sink_usec - su; + } + + if (!i->playing) + pa_smoother_pause(o->stream->smoother, x); + + /* Update the smoother */ + if ((o->stream->direction == PA_STREAM_PLAYBACK && !i->read_index_corrupt) || + (o->stream->direction == PA_STREAM_RECORD && !i->write_index_corrupt)) + pa_smoother_put(o->stream->smoother, u, calc_time(o->stream, TRUE)); + + if (i->playing) + pa_smoother_resume(o->stream->smoother, x); } } + o->stream->auto_timing_update_requested = FALSE; + if (o->stream->latency_update_callback) o->stream->latency_update_callback(o->stream, o->stream->latency_update_userdata); @@ -1223,15 +1451,15 @@ pa_operation* pa_stream_update_timing_info(pa_stream *s, pa_stream_success_cb_t if (s->direction == PA_STREAM_PLAYBACK) { /* Fill in initial correction data */ - o->stream->current_write_index_correction = cidx; - o->stream->write_index_corrections[cidx].valid = 1; - o->stream->write_index_corrections[cidx].tag = tag; - o->stream->write_index_corrections[cidx].absolute = 0; - o->stream->write_index_corrections[cidx].value = 0; - o->stream->write_index_corrections[cidx].corrupt = 0; - } -/* pa_log("requesting update %u\n", tag); */ + s->current_write_index_correction = cidx; + + s->write_index_corrections[cidx].valid = TRUE; + s->write_index_corrections[cidx].absolute = FALSE; + s->write_index_corrections[cidx].corrupt = FALSE; + s->write_index_corrections[cidx].tag = tag; + s->write_index_corrections[cidx].value = 0; + } return o; } @@ -1246,7 +1474,7 @@ void pa_stream_disconnect_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN pa_stream_ref(s); if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(s->context, command, t) < 0) + if (pa_context_handle_error(s->context, command, t, FALSE) < 0) goto finish; pa_stream_set_state(s, PA_STREAM_FAILED); @@ -1291,6 +1519,9 @@ void pa_stream_set_read_callback(pa_stream *s, pa_stream_request_cb_t cb, void * pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + s->read_callback = cb; s->read_userdata = userdata; } @@ -1299,6 +1530,9 @@ void pa_stream_set_write_callback(pa_stream *s, pa_stream_request_cb_t cb, void pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + s->write_callback = cb; s->write_userdata = userdata; } @@ -1307,6 +1541,9 @@ void pa_stream_set_state_callback(pa_stream *s, pa_stream_notify_cb_t cb, void * pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + s->state_callback = cb; s->state_userdata = userdata; } @@ -1315,6 +1552,9 @@ void pa_stream_set_overflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, voi pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + s->overflow_callback = cb; s->overflow_userdata = userdata; } @@ -1323,6 +1563,9 @@ void pa_stream_set_underflow_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + s->underflow_callback = cb; s->underflow_userdata = userdata; } @@ -1331,6 +1574,9 @@ void pa_stream_set_latency_update_callback(pa_stream *s, pa_stream_notify_cb_t c pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + s->latency_update_callback = cb; s->latency_update_userdata = userdata; } @@ -1339,6 +1585,9 @@ void pa_stream_set_moved_callback(pa_stream *s, pa_stream_notify_cb_t cb, void * pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + s->moved_callback = cb; s->moved_userdata = userdata; } @@ -1347,10 +1596,24 @@ void pa_stream_set_suspended_callback(pa_stream *s, pa_stream_notify_cb_t cb, vo pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + s->suspended_callback = cb; s->suspended_userdata = userdata; } +void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_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->started_callback = cb; + s->started_userdata = userdata; +} + void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int success = 1; @@ -1363,7 +1626,7 @@ void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UN goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; success = 0; @@ -1406,8 +1669,18 @@ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, voi 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); + if (s->smoother) { + pa_usec_t x = pa_rtclock_usec(); + + if (s->timing_info_valid) + x += s->timing_info.transport_usec; + + if (s->suspended || s->corked) + pa_smoother_pause(s->smoother, x); + } + if (s->direction == PA_STREAM_PLAYBACK) - invalidate_indexes(s, 1, 0); + invalidate_indexes(s, TRUE, FALSE); return o; } @@ -1438,23 +1711,34 @@ pa_operation* pa_stream_flush(pa_stream *s, pa_stream_success_cb_t cb, void *use pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + 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); if ((o = stream_send_simple_command(s, s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_FLUSH_PLAYBACK_STREAM : PA_COMMAND_FLUSH_RECORD_STREAM, cb, userdata))) { if (s->direction == PA_STREAM_PLAYBACK) { if (s->write_index_corrections[s->current_write_index_correction].valid) - s->write_index_corrections[s->current_write_index_correction].corrupt = 1; + s->write_index_corrections[s->current_write_index_correction].corrupt = TRUE; if (s->timing_info_valid) - s->timing_info.write_index_corrupt = 1; + s->timing_info.write_index_corrupt = TRUE; if (s->buffer_attr.prebuf > 0) - invalidate_indexes(s, 1, 0); + invalidate_indexes(s, TRUE, FALSE); else - request_auto_timing_update(s, 1); + request_auto_timing_update(s, TRUE); + + if (s->smoother && s->buffer_attr.prebuf > 0) { + pa_usec_t x = pa_rtclock_usec(); + + if (s->timing_info_valid) + x += s->timing_info.transport_usec; + + pa_smoother_pause(s->smoother, x); + } + } else - invalidate_indexes(s, 0, 1); + invalidate_indexes(s, FALSE, TRUE); } return o; @@ -1466,11 +1750,12 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + 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_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); if ((o = stream_send_simple_command(s, PA_COMMAND_PREBUF_PLAYBACK_STREAM, cb, userdata))) - invalidate_indexes(s, 1, 0); + invalidate_indexes(s, TRUE, FALSE); return o; } @@ -1481,19 +1766,18 @@ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *u pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); + 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_PLAYBACK, PA_ERR_BADSTATE); PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->buffer_attr.prebuf > 0, PA_ERR_BADSTATE); if ((o = stream_send_simple_command(s, PA_COMMAND_TRIGGER_PLAYBACK_STREAM, cb, userdata))) - invalidate_indexes(s, 1, 0); + invalidate_indexes(s, TRUE, FALSE); return o; } pa_operation* pa_stream_set_name(pa_stream *s, const char *name, 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); @@ -1502,22 +1786,32 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe 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); - o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + if (s->context->version >= 13) { + pa_proplist *p = pa_proplist_new(); - t = pa_tagstruct_command( - s->context, - s->direction == PA_STREAM_RECORD ? PA_COMMAND_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME, - &tag); - pa_tagstruct_putu32(t, s->channel); - pa_tagstruct_puts(t, name); - 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); + pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name); + o = pa_stream_proplist_update(s, PA_UPDATE_REPLACE, p, cb, userdata); + pa_proplist_free(p); + } else { + pa_tagstruct *t; + uint32_t tag; + + 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_SET_RECORD_STREAM_NAME : PA_COMMAND_SET_PLAYBACK_STREAM_NAME, + &tag); + pa_tagstruct_putu32(t, s->channel); + pa_tagstruct_puts(t, name); + 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); + } return o; } int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { - pa_usec_t usec = 0; + pa_usec_t usec; pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); @@ -1528,65 +1822,10 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) { PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_PLAYBACK || !s->timing_info.read_index_corrupt, PA_ERR_NODATA); PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_RECORD || !s->timing_info.write_index_corrupt, PA_ERR_NODATA); - if (s->cached_time_valid) - /* We alredy calculated the time value for this timing info, so let's reuse it */ - usec = s->cached_time; - else { - if (s->direction == PA_STREAM_PLAYBACK) { - /* The last byte that was written into the output device - * had this time value associated */ - usec = pa_bytes_to_usec(s->timing_info.read_index < 0 ? 0 : (uint64_t) s->timing_info.read_index, &s->sample_spec); - - if (!s->corked) { - /* Because the latency info took a little time to come - * to us, we assume that the real output time is actually - * a little ahead */ - usec += s->timing_info.transport_usec; - - /* However, the output device usually maintains a buffer - too, hence the real sample currently played is a little - back */ - if (s->timing_info.sink_usec >= usec) - usec = 0; - else - usec -= s->timing_info.sink_usec; - } - - } else if (s->direction == PA_STREAM_RECORD) { - /* The last byte written into the server side queue had - * this time value associated */ - usec = pa_bytes_to_usec(s->timing_info.write_index < 0 ? 0 : (uint64_t) s->timing_info.write_index, &s->sample_spec); - - if (!s->corked) { - /* Add transport latency */ - usec += s->timing_info.transport_usec; - - /* Add latency of data in device buffer */ - usec += s->timing_info.source_usec; - - /* If this is a monitor source, we need to correct the - * time by the playback device buffer */ - if (s->timing_info.sink_usec >= usec) - usec = 0; - else - usec -= s->timing_info.sink_usec; - } - } - - s->cached_time = usec; - s->cached_time_valid = 1; - } - - /* Interpolate if requested */ - if (s->flags & PA_STREAM_INTERPOLATE_TIMING) { - - /* We just add the time that passed since the latency info was - * current */ - if (!s->corked && s->timing_info.playing) { - struct timeval now; - usec += pa_timeval_diff(pa_gettimeofday(&now), &s->timing_info.timestamp); - } - } + if (s->smoother) + usec = pa_smoother_get(s->smoother, pa_rtclock_usec()); + else + usec = calc_time(s, FALSE); /* Make sure the time runs monotonically */ if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) { @@ -1687,7 +1926,7 @@ const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) { 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 >= 9, PA_ERR_NODATA); + PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->context->version >= 9, PA_ERR_NOTSUPPORTED); return &s->buffer_attr; } @@ -1704,7 +1943,7 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; success = 0; @@ -1730,8 +1969,6 @@ static void stream_set_buffer_attr_callback(pa_pdispatch *pd, uint32_t command, pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } - - o->stream->manual_buffer_attr = TRUE; } if (o->callback) { @@ -1822,6 +2059,16 @@ int pa_stream_is_suspended(pa_stream *s) { return s->suspended; } +int pa_stream_is_corked(pa_stream *s) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_BADSTATE); + + return s->corked; +} + static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int success = 1; @@ -1834,7 +2081,7 @@ static void stream_update_sample_rate_callback(pa_pdispatch *pd, uint32_t comman goto finish; if (command != PA_COMMAND_REPLY) { - if (pa_context_handle_error(o->context, command, t) < 0) + if (pa_context_handle_error(o->context, command, t, FALSE) < 0) goto finish; success = 0; diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 69943a70..ebb45f2b 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -339,6 +339,10 @@ const char *pa_stream_get_device_name(pa_stream *s); * server is older than 0.9.8. \since 0.9.8 */ int pa_stream_is_suspended(pa_stream *s); +/** Return 1 if the this stream has been corked. This will return 0 if + * not, and negative on error. \since 0.9.11 */ +int pa_stream_is_corked(pa_stream *s); + /** Connect the stream to a sink */ int pa_stream_connect_playback( pa_stream *s /**< The stream to connect to a sink */, @@ -368,7 +372,7 @@ int pa_stream_disconnect(pa_stream *s); int pa_stream_write( pa_stream *p /**< The stream to use */, const void *data /**< The data to write */, - size_t bytes /**< The length of the data to write in bytes*/, + size_t nbytes /**< The length of the data to write in bytes*/, pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */, int64_t offset, /**< Offset for seeking, must be 0 for upload streams */ pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */); @@ -381,7 +385,7 @@ int pa_stream_write( int pa_stream_peek( pa_stream *p /**< The stream to use */, const void **data /**< Pointer to pointer that will point to data */, - size_t *bytes /**< The length of the data read in bytes */); + size_t *nbytes /**< The length of the data read in bytes */); /** Remove the current fragment on record streams. It is invalid to do this without first * calling pa_stream_peek(). */ @@ -419,6 +423,13 @@ void pa_stream_set_overflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, voi /** Set the callback function that is called when a buffer underflow happens. (Only for playback streams) */ void pa_stream_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); +/** Set the callback function that is called when a the server starts + * playback after an underrun or on initial startup. This only informs + * that audio is flowing again, it is no indication that audio startet + * to reach the speakers already. (Only for playback streams). \since + * 0.9.11 */ +void pa_stream_set_started_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata); + /** Set the callback function that is called whenever a latency * information update happens. Useful on PA_STREAM_AUTO_TIMING_UPDATE * streams only. (Only for playback streams) */ diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index 28885b2c..df110966 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -41,6 +41,7 @@ #include #include #include +#include #ifdef HAVE_STRTOF_L #include @@ -103,12 +104,6 @@ #define MSG_NOSIGNAL 0 #endif -#ifndef OS_IS_WIN32 -#define PA_USER_RUNTIME_PATH_PREFIX "/tmp/pulse-" -#else -#define PA_USER_RUNTIME_PATH_PREFIX "%TEMP%\\pulse-" -#endif - #ifdef OS_IS_WIN32 #define PULSE_ROOTENV "PULSE_ROOT" @@ -221,7 +216,7 @@ int pa_make_secure_dir(const char* dir, mode_t m, uid_t uid, gid_t gid) { goto fail; } #else - pa_log_warn("secure directory creation not supported on Win32."); + pa_log_warn("Secure directory creation not supported on Win32."); #endif return 0; @@ -557,6 +552,82 @@ int pa_make_realtime(int rtprio) { #endif } +/* This is merely used for giving the user a hint. This is not correct + * for anything security related */ +pa_bool_t pa_can_realtime(void) { + + if (geteuid() == 0) + return TRUE; + +#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) + { + struct rlimit rl; + + if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) + if (rl.rlim_cur > 0 || rl.rlim_cur == RLIM_INFINITY) + return TRUE; + } +#endif + +#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) + { + cap_t cap; + + if ((cap = cap_get_proc())) { + cap_flag_value_t flag = CAP_CLEAR; + + if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) + if (flag == CAP_SET) { + cap_free(cap); + return TRUE; + } + + cap_free(cap); + } + } +#endif + + return FALSE; +} + +/* This is merely used for giving the user a hint. This is not correct + * for anything security related */ +pa_bool_t pa_can_high_priority(void) { + + if (geteuid() == 0) + return TRUE; + +#if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_RTPRIO) + { + struct rlimit rl; + + if (getrlimit(RLIMIT_NICE, &rl) >= 0) + if (rl.rlim_cur >= 21 || rl.rlim_cur == RLIM_INFINITY) + return TRUE; + } +#endif + +#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_SYS_NICE) + { + cap_t cap; + + if ((cap = cap_get_proc())) { + cap_flag_value_t flag = CAP_CLEAR; + + if (cap_get_flag(cap, CAP_SYS_NICE, CAP_EFFECTIVE, &flag) >= 0) + if (flag == CAP_SET) { + cap_free(cap); + return TRUE; + } + + cap_free(cap); + } + } +#endif + + return FALSE; +} + /* Raise the priority of the current process as much as possible that * is <= the specified nice level..*/ int pa_raise_priority(int nice_level) { @@ -612,6 +683,7 @@ void pa_reset_priority(void) { /* Try to parse a boolean string value.*/ int pa_parse_boolean(const char *v) { + pa_assert(v); if (!strcmp(v, "1") || v[0] == 'y' || v[0] == 'Y' || v[0] == 't' || v[0] == 'T' || !strcasecmp(v, "on")) return 1; @@ -1093,11 +1165,11 @@ int pa_unlock_lockfile(const char *fn, int fd) { return r; } -char *pa_get_state_dir(void) { +char *pa_get_runtime_dir(void) { const char *e; char *d; - if ((e = getenv("PULSE_STATE_PATH"))) + if ((e = getenv("PULSE_RUNTIME_PATH"))) d = pa_xstrdup(e); else { char h[PATH_MAX]; @@ -1107,19 +1179,15 @@ char *pa_get_state_dir(void) { return NULL; } - d = pa_sprintf_malloc("%s/.pulse", h); + d = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h); } - mkdir(d, 0755); - - if (access(d, W_OK) == 0) - return d; - - pa_log_error("Failed to set up state directory %s", d); - - pa_xfree(d); + if (pa_make_secure_dir(d, 0700, (pid_t) -1, (pid_t) -1) < 0) { + pa_log_error("Failed to create secure directory: %s", pa_cstrerror(errno)); + return NULL; + } - return NULL; + return d; } /* Try to open a configuration file. If "env" is specified, open the @@ -1128,10 +1196,8 @@ char *pa_get_state_dir(void) { * file system. If "result" is non-NULL, a pointer to a newly * allocated buffer containing the used configuration file is * stored there.*/ -FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode) { +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result) { const char *fn; - char h[PATH_MAX]; - #ifdef OS_IS_WIN32 char buf[PATH_MAX]; @@ -1140,75 +1206,152 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env #endif if (env && (fn = getenv(env))) { + FILE *f; + #ifdef OS_IS_WIN32 if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) return NULL; fn = buf; #endif - if (result) - *result = pa_xstrdup(fn); + if ((f = fopen(fn, "r"))) { + if (result) + *result = pa_xstrdup(fn); + + return f; + } - return fopen(fn, mode); + pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + return NULL; } if (local) { const char *e; - char *lfn = NULL; + char *lfn; + char h[PATH_MAX]; + FILE *f; if ((e = getenv("PULSE_CONFIG_PATH"))) - fn = lfn = pa_sprintf_malloc("%s/%s", e, local); - else if (pa_get_home_dir(h, sizeof(h))) { - char *d; + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local); + else if (pa_get_home_dir(h, sizeof(h))) + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); - d = pa_sprintf_malloc("%s/.pulse", h); - mkdir(d, 0755); - pa_xfree(d); +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) { + pa_xfree(lfn); + return NULL; + } + fn = buf; +#endif + + if ((f = fopen(fn, "r"))) { + if (result) + *result = pa_xstrdup(fn); + + pa_xfree(lfn); + return f; + } - fn = lfn = pa_sprintf_malloc("%s/.pulse/%s", h, local); + if (errno != ENOENT) { + pa_log_warn("Failed to open configuration file '%s': %s", fn, pa_cstrerror(errno)); + pa_xfree(lfn); + return NULL; } - if (lfn) { - FILE *f; + pa_xfree(lfn); + } + + if (global) { + FILE *f; #ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) - return NULL; - fn = buf; + if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) + return NULL; + global = buf; #endif - f = fopen(fn, mode); - if (f != NULL) { - if (result) - *result = pa_xstrdup(fn); - pa_xfree(lfn); - return f; - } + if ((f = fopen(global, "r"))) { - if (errno != ENOENT) - pa_log_warn("Failed to open configuration file '%s': %s", lfn, pa_cstrerror(errno)); + if (result) + *result = pa_xstrdup(global); - pa_xfree(lfn); + return f; } - } - - if (!global) { - if (result) - *result = NULL; + } else errno = ENOENT; + + return NULL; +} + +char *pa_find_config_file(const char *global, const char *local, const char *env) { + const char *fn; +#ifdef OS_IS_WIN32 + char buf[PATH_MAX]; + + if (!getenv(PULSE_ROOTENV)) + pa_set_root(NULL); +#endif + + if (env && (fn = getenv(env))) { +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(fn, buf, PATH_MAX)) + return NULL; + fn = buf; +#endif + + if (access(fn, R_OK) == 0) + return pa_xstrdup(fn); + + pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno)); return NULL; } + if (local) { + const char *e; + char *lfn; + char h[PATH_MAX]; + + if ((e = getenv("PULSE_CONFIG_PATH"))) + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local); + else if (pa_get_home_dir(h, sizeof(h))) + fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local); + #ifdef OS_IS_WIN32 - if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) - return NULL; - global = buf; + if (!ExpandEnvironmentStrings(lfn, buf, PATH_MAX)) { + pa_xfree(lfn); + return NULL; + } + fn = buf; #endif - if (result) - *result = pa_xstrdup(global); + if (access(fn, R_OK) == 0) { + char *r = pa_xstrdup(fn); + pa_xfree(lfn); + return r; + } + + if (errno != ENOENT) { + pa_log_warn("Failed to access configuration file '%s': %s", fn, pa_cstrerror(errno)); + pa_xfree(lfn); + return NULL; + } + + pa_xfree(lfn); + } + + if (global) { +#ifdef OS_IS_WIN32 + if (!ExpandEnvironmentStrings(global, buf, PATH_MAX)) + return NULL; + global = buf; +#endif + + if (access(fn, R_OK) == 0) + return pa_xstrdup(global); + } else + errno = ENOENT; - return fopen(global, mode); + return NULL; } /* Format the specified data as a hexademical string */ @@ -1299,45 +1442,51 @@ int pa_endswith(const char *s, const char *sfx) { return l1 >= l2 && strcmp(s+l1-l2, sfx) == 0; } -/* if fn is null return the PulseAudio run time path in s (/tmp/pulse) - * if fn is non-null and starts with / return fn in s - * otherwise append fn to the run time path and return it in s */ -char *pa_runtime_path(const char *fn, char *s, size_t l) { - const char *e; +pa_bool_t pa_is_path_absolute(const char *fn) { + pa_assert(fn); #ifndef OS_IS_WIN32 - if (fn && *fn == '/') + return *fn == '/'; #else - if (fn && strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\') + return strlen(fn) >= 3 && isalpha(fn[0]) && fn[1] == ':' && fn[2] == '\\'; #endif - return pa_strlcpy(s, fn, l); +} - if ((e = getenv("PULSE_RUNTIME_PATH"))) { +char *pa_make_path_absolute(const char *p) { + char *r; + char *cwd; - if (fn) - pa_snprintf(s, l, "%s%c%s", e, PA_PATH_SEP_CHAR, fn); - else - pa_snprintf(s, l, "%s", e); + pa_assert(p); - } else { - char u[256]; + if (pa_is_path_absolute(p)) + return pa_xstrdup(p); - if (fn) - pa_snprintf(s, l, "%s%s%c%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u)), PA_PATH_SEP_CHAR, fn); - else - pa_snprintf(s, l, "%s%s", PA_USER_RUNTIME_PATH_PREFIX, pa_get_user_name(u, sizeof(u))); - } + if (!(cwd = pa_getcwd())) + return pa_xstrdup(p); + r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", cwd, p); + pa_xfree(cwd); + return r; +} -#ifdef OS_IS_WIN32 - { - char buf[l]; - strcpy(buf, s); - ExpandEnvironmentStrings(buf, s, l); - } -#endif +/* if fn is null return the PulseAudio run time path in s (~/.pulse) + * if fn is non-null and starts with / return fn + * otherwise append fn to the run time path and return it */ +char *pa_runtime_path(const char *fn) { + char *rtp; - return s; + if (pa_is_path_absolute(fn)) + return pa_xstrdup(fn); + + rtp = pa_get_runtime_dir(); + + if (fn) { + char *r; + r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn); + pa_xfree(rtp); + return r; + } else + return rtp; } /* Convert the string s to a signed integer in *ret_i */ @@ -1484,23 +1633,6 @@ char *pa_getcwd(void) { } } -char *pa_make_path_absolute(const char *p) { - char *r; - char *cwd; - - pa_assert(p); - - if (p[0] == '/') - return pa_xstrdup(p); - - if (!(cwd = pa_getcwd())) - return pa_xstrdup(p); - - r = pa_sprintf_malloc("%s/%s", cwd, p); - pa_xfree(cwd); - return r; -} - void *pa_will_need(const void *p, size_t l) { #ifdef RLIMIT_MEMLOCK struct rlimit rlim; @@ -1606,3 +1738,249 @@ char *pa_readlink(const char *p) { l *= 2; } } + +int pa_close_all(int except_fd, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except_fd); + + if (except_fd >= 0) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except_fd); + + i = 0; + if (except_fd >= 0) { + p[i++] = except_fd; + + while ((p[i++] = va_arg(ap, int)) >= 0) + ; + } + p[i] = -1; + + va_end(ap); + + r = pa_close_allv(p); + free(p); + + return r; +} + +int pa_close_allv(const int except_fds[]) { + struct rlimit rl; + int fd; + int saved_errno; + +#ifdef __linux__ + + DIR *d; + + if ((d = opendir("/proc/self/fd"))) { + + struct dirent *de; + + while ((de = readdir(d))) { + long l; + char *e = NULL; + int i; + + if (de->d_name[0] == '.') + continue; + + errno = 0; + l = strtol(de->d_name, &e, 10); + if (errno != 0 || !e || *e) { + closedir(d); + errno = EINVAL; + return -1; + } + + fd = (int) l; + + if ((long) fd != l) { + closedir(d); + errno = EINVAL; + return -1; + } + + if (fd <= 3) + continue; + + if (fd == dirfd(d)) + continue; + + for (i = 0; except_fds[i] >= 0; i++) + if (except_fds[i] == fd) + continue; + + if (close(fd) < 0) { + saved_errno = errno; + closedir(d); + errno = saved_errno; + + return -1; + } + } + + closedir(d); + return 0; + } + +#endif + + if (getrlimit(RLIMIT_NOFILE, &rl) < 0) + return -1; + + for (fd = 0; fd < (int) rl.rlim_max; fd++) { + int i; + + if (fd <= 3) + continue; + + for (i = 0; except_fds[i] >= 0; i++) + if (except_fds[i] == fd) + continue; + + if (close(fd) < 0 && errno != EBADF) + return -1; + } + + return 0; +} + +int pa_unblock_sigs(int except, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except); + + if (except >= 1) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except); + + i = 0; + if (except >= 1) { + p[i++] = except; + + while ((p[i++] = va_arg(ap, int)) >= 0) + ; + } + p[i] = -1; + + va_end(ap); + + r = pa_unblock_sigsv(p); + pa_xfree(p); + + return r; +} + +int pa_unblock_sigsv(const int except[]) { + int i; + sigset_t ss; + + if (sigemptyset(&ss) < 0) + return -1; + + for (i = 0; except[i] > 0; i++) + if (sigaddset(&ss, except[i]) < 0) + return -1; + + return sigprocmask(SIG_SETMASK, &ss, NULL); +} + +int pa_reset_sigs(int except, ...) { + va_list ap; + int n = 0, i, r; + int *p; + + va_start(ap, except); + + if (except >= 1) + for (n = 1; va_arg(ap, int) >= 0; n++) + ; + + va_end(ap); + + p = pa_xnew(int, n+1); + + va_start(ap, except); + + i = 0; + if (except >= 1) { + p[i++] = except; + + while ((p[i++] = va_arg(ap, int)) >= 0) + ; + } + p[i] = -1; + + va_end(ap); + + r = pa_reset_sigsv(p); + pa_xfree(p); + + return r; +} + +int pa_reset_sigsv(const int except[]) { + int sig; + + for (sig = 1; sig < _NSIG; sig++) { + int reset = 1; + + switch (sig) { + case SIGKILL: + case SIGSTOP: + reset = 0; + break; + + default: { + int i; + + for (i = 0; except[i] > 0; i++) { + if (sig == except[i]) { + reset = 0; + break; + } + } + } + } + + if (reset) { + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + + /* On Linux the first two RT signals are reserved by + * glibc, and sigaction() will return EINVAL for them. */ + if ((sigaction(sig, &sa, NULL) < 0)) + if (errno != EINVAL) + return -1; + } + } + + return 0; +} + +void pa_set_env(const char *key, const char *value) { + pa_assert(key); + pa_assert(value); + + putenv(pa_sprintf_malloc("%s=%s", key, value)); +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index d5c0a3f6..49315b55 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -30,11 +30,27 @@ #include #include +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif + #include #include struct timeval; +/* These resource limits are pretty new on Linux, let's define them + * here manually, in case the kernel is newer than the glibc */ +#if !defined(RLIMIT_NICE) && defined(__linux__) +#define RLIMIT_NICE 13 +#endif +#if !defined(RLIMIT_RTPRIO) && defined(__linux__) +#define RLIMIT_RTPRIO 14 +#endif +#if !defined(RLIMIT_RTTIME) && defined(__linux__) +#define RLIMIT_RTTIME 15 +#endif + void pa_make_fd_nonblock(int fd); void pa_make_fd_cloexec(int fd); @@ -61,6 +77,9 @@ int pa_make_realtime(int rtprio); int pa_raise_priority(int nice_level); void pa_reset_priority(void); +pa_bool_t pa_can_realtime(void); +pa_bool_t pa_can_high_priority(void); + int pa_parse_boolean(const char *s) PA_GCC_PURE; static inline const char *pa_yes_no(pa_bool_t b) { @@ -71,6 +90,10 @@ static inline const char *pa_strnull(const char *x) { return x ? x : "(null)"; } +static inline const char *pa_strempty(const char *x) { + return x ? x : ""; +} + char *pa_split(const char *c, const char*delimiters, const char **state); char *pa_split_spaces(const char *c, const char **state); @@ -88,15 +111,17 @@ int pa_lock_fd(int fd, int b); int pa_lock_lockfile(const char *fn); int pa_unlock_lockfile(const char *fn, int fd); -FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result, const char *mode); - char *pa_hexstr(const uint8_t* d, size_t dlength, char *s, size_t slength); size_t pa_parsehex(const char *p, uint8_t *d, size_t dlength); int pa_startswith(const char *s, const char *pfx) PA_GCC_PURE; int pa_endswith(const char *s, const char *sfx) PA_GCC_PURE; -char *pa_runtime_path(const char *fn, char *s, size_t l); +FILE *pa_open_config_file(const char *global, const char *local, const char *env, char **result); +char* pa_find_config_file(const char *global, const char *local, const char *env); + +char *pa_get_runtime_dir(void); +char *pa_runtime_path(const char *fn); int pa_atoi(const char *s, int32_t *ret_i); int pa_atou(const char *s, uint32_t *ret_u); @@ -108,6 +133,7 @@ char *pa_truncate_utf8(char *c, size_t l); char *pa_getcwd(void); char *pa_make_path_absolute(const char *p); +pa_bool_t pa_is_path_absolute(const char *p); void *pa_will_need(const void *p, size_t l); @@ -133,6 +159,13 @@ void pa_close_pipe(int fds[2]); char *pa_readlink(const char *p); -char *pa_get_state_dir(void); +int pa_close_all(int except_fd, ...); +int pa_close_allv(const int except_fds[]); +int pa_unblock_sigs(int except, ...); +int pa_unblock_sigsv(const int except[]); +int pa_reset_sigs(int except, ...); +int pa_reset_sigsv(const int except[]); + +void pa_set_env(const char *key, const char *value); #endif diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 51f2b309..56f9037e 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -147,6 +147,9 @@ enum { PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST, PA_COMMAND_REMOVE_CLIENT_PROPLIST, + /* SERVER->CLIENT */ + PA_COMMAND_STARTED, + PA_COMMAND_MAX }; diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c index f3c9faaa..2ff132bb 100644 --- a/src/pulsecore/pid.c +++ b/src/pulsecore/pid.c @@ -144,16 +144,16 @@ fail: int pa_pid_file_create(void) { int fd = -1; int ret = -1; - char fn[PATH_MAX]; char t[20]; pid_t pid; size_t l; + char *fn; #ifdef OS_IS_WIN32 HANDLE process; #endif - pa_runtime_path("pid", fn, sizeof(fn)); + fn = pa_runtime_path("pid"); if ((fd = open_pid_file(fn, O_CREAT|O_RDWR)) < 0) goto fail; @@ -200,17 +200,19 @@ fail: } } + pa_xfree(fn); + return ret; } /* Remove the PID file, if it is ours */ int pa_pid_file_remove(void) { int fd = -1; - char fn[PATH_MAX]; + char *fn; int ret = -1; pid_t pid; - pa_runtime_path("pid", fn, sizeof(fn)); + fn = pa_runtime_path("pid"); if ((fd = open_pid_file(fn, O_RDWR)) < 0) { pa_log_warn("Failed to open PID file '%s': %s", fn, pa_cstrerror(errno)); @@ -254,6 +256,8 @@ fail: } } + pa_xfree(fn); + return ret; } @@ -272,7 +276,7 @@ int pa_pid_file_check_running(pid_t *pid, const char *binary_name) { * process. */ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { int fd = -1; - char fn[PATH_MAX]; + char *fn; int ret = -1; pid_t _pid; #ifdef __linux__ @@ -281,7 +285,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { if (!pid) pid = &_pid; - pa_runtime_path("pid", fn, sizeof(fn)); + fn = pa_runtime_path("pid"); if ((fd = open_pid_file(fn, O_RDONLY)) < 0) goto fail; @@ -296,7 +300,7 @@ int pa_pid_file_kill(int sig, pid_t *pid, const char *binary_name) { if ((e = pa_readlink(fn))) { char *f = pa_path_get_filename(e); if (strcmp(f, binary_name) -#if defined(__OPTIMIZE__) +#if !defined(__OPTIMIZE__) /* libtool likes to rename our binary names ... */ && !(pa_startswith(f, "lt-") && strcmp(f+3, binary_name) == 0) #endif @@ -319,6 +323,8 @@ fail: pa_xfree(e); #endif + pa_xfree(fn); + return ret; } diff --git a/src/pulsecore/protocol-cli.c b/src/pulsecore/protocol-cli.c index ceb6ae4d..2f797a14 100644 --- a/src/pulsecore/protocol-cli.c +++ b/src/pulsecore/protocol-cli.c @@ -82,7 +82,7 @@ pa_protocol_cli* pa_protocol_cli_new(pa_core *core, pa_socket_server *server, pa p = pa_xnew(pa_protocol_cli, 1); p->module = m; p->core = core; - p->server = server; + p->server = pa_socket_server_ref(server); p->connections = pa_idxset_new(NULL, NULL); pa_socket_server_set_callback(p->server, on_connection, p); diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 59a4208f..388808a5 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -1433,7 +1433,7 @@ pa_protocol_esound* pa_protocol_esound_new(pa_core*core, pa_socket_server *serve p->core = core; p->module = m; p->public = public; - p->server = server; + p->server = pa_socket_server_ref(server); pa_socket_server_set_callback(p->server, on_connection, p); p->connections = pa_idxset_new(NULL, NULL); diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c index 589eba4f..bc2e9af6 100644 --- a/src/pulsecore/protocol-http.c +++ b/src/pulsecore/protocol-http.c @@ -255,7 +255,7 @@ pa_protocol_http* pa_protocol_http_new(pa_core *core, pa_socket_server *server, p = pa_xnew(pa_protocol_http, 1); p->module = m; p->core = core; - p->server = server; + p->server = pa_socket_server_ref(server); p->connections = pa_idxset_new(NULL, NULL); pa_socket_server_set_callback(p->server, on_connection, p); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index ca14b955..5fee4ccf 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -105,7 +105,6 @@ typedef struct playback_stream { pa_bool_t drain_request; uint32_t drain_tag; uint32_t syncid; - uint64_t underrun; /* length of underrun */ pa_atomic_t missing; size_t minreq; @@ -193,7 +192,8 @@ enum { PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, /* data requested from sink input from the main loop */ PLAYBACK_STREAM_MESSAGE_UNDERFLOW, PLAYBACK_STREAM_MESSAGE_OVERFLOW, - PLAYBACK_STREAM_MESSAGE_DRAIN_ACK + PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, + PLAYBACK_STREAM_MESSAGE_STARTED }; enum { @@ -689,10 +689,24 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, break; } + case PLAYBACK_STREAM_MESSAGE_STARTED: + + if (s->connection->version >= 13) { + pa_tagstruct *t; + + /* Notify the user we're overflowed*/ + t = pa_tagstruct_new(NULL, 0); + pa_tagstruct_putu32(t, PA_COMMAND_STARTED); + pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */ + pa_tagstruct_putu32(t, s->index); + pa_pstream_send_tagstruct(s->connection->pstream, t); + } + + break; + case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK: pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata)); break; - } return 0; @@ -886,7 +900,6 @@ static playback_stream* playback_stream_new( s->connection = c; s->syncid = syncid; s->sink_input = sink_input; - s->underrun = (uint64_t) -1; s->sink_input->parent.process_msg = sink_input_process_msg; s->sink_input->pop = sink_input_pop_cb; @@ -1091,7 +1104,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { /* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->underrun, pa_memblockq_is_readable(s->memblockq)); */ - if (s->underrun != 0) { + if (s->sink_input->thread_info.underrun_for > 0) { /* pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */ @@ -1099,13 +1112,13 @@ static void handle_seek(playback_stream *s, int64_t indexw) { size_t u = pa_memblockq_get_length(s->memblockq); - if (u >= s->underrun) - u = s->underrun; + if (u >= s->sink_input->thread_info.underrun_for) + u = s->sink_input->thread_info.underrun_for; /* We just ended an underrun, let's ask the sink * to rewrite */ - s->sink_input->thread_info.ignore_rewind = TRUE; - pa_sink_input_request_rewind(s->sink_input, u, TRUE); + + pa_sink_input_request_rewind(s->sink_input, u, TRUE, TRUE); } } else { @@ -1117,7 +1130,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { /* OK, the sink already asked for this data, so * let's have it usk us again */ - pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE); + pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE, FALSE); } request_bytes(s); @@ -1272,12 +1285,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (s->drain_request && pa_sink_input_safe_to_remove(i)) { s->drain_request = FALSE; pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL); - } else if (s->underrun == 0) + } else if (i->thread_info.playing_for > 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL); - if (s->underrun != (size_t) -1) - s->underrun += nbytes; - /* pa_log("added %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) s->underrun); */ request_bytes(s); @@ -1287,7 +1297,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk /* pa_log("NOTUNDERRUN"); */ - s->underrun = 0; + if (i->thread_info.underrun_for > 0) + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL); pa_memblockq_drop(s->memblockq, chunk->length); request_bytes(s); @@ -1303,7 +1314,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { playback_stream_assert_ref(s); /* If we are in an underrun, then we don't rewind */ - if (s->underrun != 0) + if (i->thread_info.underrun_for > 0) return; pa_memblockq_rewind(s->memblockq, nbytes); @@ -2120,11 +2131,17 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_ pa_tagstruct_put_usec(reply, latency); pa_tagstruct_put_usec(reply, 0); - pa_tagstruct_put_boolean(reply, pa_sink_input_get_state(s->sink_input) == PA_SINK_INPUT_RUNNING); + pa_tagstruct_put_boolean(reply, s->sink_input->thread_info.playing_for > 0); pa_tagstruct_put_timeval(reply, &tv); pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now)); pa_tagstruct_puts64(reply, s->write_index); pa_tagstruct_puts64(reply, s->read_index); + + if (c->version >= 13) { + pa_tagstruct_putu64(reply, s->sink_input->thread_info.underrun_for); + pa_tagstruct_putu64(reply, s->sink_input->thread_info.playing_for); + } + pa_pstream_send_tagstruct(c->pstream, reply); } @@ -2152,7 +2169,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN reply = reply_new(tag); pa_tagstruct_put_usec(reply, s->source_output->source->monitor_of ? pa_sink_get_latency(s->source_output->source->monitor_of) : 0); pa_tagstruct_put_usec(reply, pa_source_get_latency(s->source_output->source)); - pa_tagstruct_put_boolean(reply, FALSE); + pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING); pa_tagstruct_put_timeval(reply, &tv); pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now)); pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq)); @@ -3937,7 +3954,7 @@ static pa_protocol_native* protocol_new_internal(pa_core *c, pa_module *m, pa_mo #ifdef HAVE_CREDS { - pa_bool_t a = 1; + pa_bool_t a = TRUE; if (pa_modargs_get_value_boolean(ma, "auth-group-enabled", &a) < 0) { pa_log("auth-group-enabled= expects a boolean argument."); return NULL; @@ -3982,7 +3999,7 @@ pa_protocol_native* pa_protocol_native_new(pa_core *core, pa_socket_server *serv if (!(p = protocol_new_internal(core, m, ma))) return NULL; - p->server = server; + p->server = pa_socket_server_ref(server); pa_socket_server_set_callback(p->server, on_connection, p); if (pa_socket_server_get_address(p->server, t, sizeof(t))) { diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 3ee2a058..8ec38fe4 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -587,7 +587,7 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv p = pa_xnew0(pa_protocol_simple, 1); p->module = m; p->core = core; - p->server = server; + p->server = pa_socket_server_ref(server); p->connections = pa_idxset_new(NULL, NULL); p->sample_spec = core->default_sample_spec; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 8df36876..1da920a9 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -106,6 +106,7 @@ static void reset_callbacks(pa_sink_input *i) { i->moved = NULL; i->kill = NULL; i->get_latency = NULL; + i->state_change = NULL; } pa_sink_input* pa_sink_input_new( @@ -249,8 +250,8 @@ pa_sink_input* pa_sink_input_new( i->thread_info.muted = i->muted; i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.rewrite_nbytes = 0; - i->thread_info.since_underrun = 0; - i->thread_info.ignore_rewind = FALSE; + i->thread_info.underrun_for = (uint64_t) -1; + i->thread_info.playing_for = 0; i->thread_info.render_memblockq = pa_memblockq_new( 0, @@ -328,7 +329,7 @@ void pa_sink_input_unlink(pa_sink_input *i) { pa_sink_input_ref(i); - linked = PA_SINK_INPUT_LINKED(i->state); + linked = PA_SINK_INPUT_IS_LINKED(i->state); if (linked) pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i); @@ -344,12 +345,11 @@ void pa_sink_input_unlink(pa_sink_input *i) { if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL)) pa_sink_input_unref(i); - if (linked) { + update_n_corked(i, PA_SINK_INPUT_UNLINKED); + i->state = PA_SINK_INPUT_UNLINKED; + + if (linked) pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL); - sink_input_set_state(i, PA_SINK_INPUT_UNLINKED); - pa_sink_update_status(i->sink); - } else - i->state = PA_SINK_INPUT_UNLINKED; reset_callbacks(i); @@ -368,7 +368,7 @@ static void sink_input_free(pa_object *o) { pa_assert(i); pa_assert(pa_sink_input_refcnt(i) == 0); - if (PA_SINK_INPUT_LINKED(i->state)) + if (PA_SINK_INPUT_IS_LINKED(i->state)) pa_sink_input_unlink(i); pa_log_info("Freeing input %u \"%s\"", i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME))); @@ -402,7 +402,7 @@ void pa_sink_input_put(pa_sink_input *i) { state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; update_n_corked(i, state); - i->thread_info.state = i->state = state; + i->state = state; pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); @@ -416,7 +416,7 @@ void pa_sink_input_put(pa_sink_input *i) { void pa_sink_input_kill(pa_sink_input*i) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); if (i->kill) i->kill(i); @@ -426,7 +426,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i) { pa_usec_t r = 0; pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); if (pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) r = 0; @@ -445,7 +445,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa size_t ilength; pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(slength, &i->sink->sample_spec)); pa_assert(chunk); pa_assert(volume); @@ -510,7 +510,9 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_atomic_store(&i->thread_info.drained, 1); pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ); - i->thread_info.since_underrun = 0; + i->thread_info.playing_for = 0; + if (i->thread_info.underrun_for != (uint64_t) -1) + i->thread_info.underrun_for += slength; break; } @@ -519,7 +521,8 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_assert(tchunk.length > 0); pa_assert(tchunk.memblock); - i->thread_info.since_underrun += tchunk.length; + i->thread_info.underrun_for = 0; + i->thread_info.playing_for += tchunk.length; while (tchunk.length > 0) { pa_memchunk wchunk; @@ -590,7 +593,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); pa_assert(nbytes > 0); @@ -610,13 +613,13 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec * void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); /* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */ - if (i->thread_info.ignore_rewind) { - i->thread_info.ignore_rewind = FALSE; + if (i->thread_info.underrun_for > 0) { + /* We don't rewind when we are underrun */ i->thread_info.rewrite_nbytes = 0; return; } @@ -668,7 +671,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam /* Called from thread context */ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); pa_memblockq_set_maxrewind(i->thread_info.render_memblockq, nbytes); @@ -677,21 +680,41 @@ void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the i->update_max_rewind(i, i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, nbytes) : nbytes); } -pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { - pa_sink_input_assert_ref(i); +static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) { + pa_sink_assert_ref(s); - if (usec != (pa_usec_t) -1) { + if (usec == (pa_usec_t) -1) + return usec; - if (i->sink->max_latency > 0 && usec > i->sink->max_latency) - usec = i->sink->max_latency; + if (s->max_latency > 0 && usec > s->max_latency) + usec = s->max_latency; - if (i->sink->min_latency > 0 && usec < i->sink->min_latency) - usec = i->sink->min_latency; - } + if (s->min_latency > 0 && usec < s->min_latency) + usec = s->min_latency; + + return usec; +} + +pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) { + + usec = fixup_latency(i->sink, usec); + + i->thread_info.requested_sink_latency = usec; + pa_sink_invalidate_requested_latency(i->sink); - if (PA_SINK_INPUT_LINKED(i->state)) + return usec; +} + +pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) { + pa_sink_input_assert_ref(i); + + usec = fixup_latency(i->sink, usec); + + if (PA_SINK_INPUT_IS_LINKED(i->state)) pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); else { + /* If this sink input is not realized yet, we have to touch + * the thread info data directly */ i->thread_info.requested_sink_latency = usec; i->sink->thread_info.requested_latency_valid = FALSE; } @@ -701,7 +724,7 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); if (pa_cvolume_equal(&i->volume, volume)) return; @@ -714,7 +737,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); return &i->volume; } @@ -722,7 +745,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) { void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { pa_assert(i); pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); if (!i->muted == !mute) return; @@ -735,21 +758,21 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) { int pa_sink_input_get_mute(pa_sink_input *i) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); return !!i->muted; } void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); sink_input_set_state(i, b ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING); } int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) { pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); pa_return_val_if_fail(i->thread_info.resampler, -1); if (i->sample_spec.rate == rate) @@ -780,7 +803,7 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) { else pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME); - if (PA_SINK_INPUT_LINKED(i->state)) { + if (PA_SINK_INPUT_IS_LINKED(i->state)) { pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i); pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); } @@ -792,7 +815,7 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { return i->resample_method; } -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately) { pa_resampler *new_resampler; pa_sink *origin; pa_usec_t silence_usec = 0; @@ -800,7 +823,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { pa_sink_input_move_hook_data hook_data; pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); pa_sink_assert_ref(dest); origin = i->sink; @@ -983,7 +1006,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately) { return 0; } -static void set_state(pa_sink_input *i, pa_sink_input_state_t state) { +void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) { pa_sink_input_assert_ref(i); if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) && @@ -998,17 +1021,18 @@ static void set_state(pa_sink_input *i, pa_sink_input_state_t state) { /* This will tell the implementing sink input driver to rewind * so that the unplayed already mixed data is not lost */ - pa_sink_input_request_rewind(i, 0, FALSE); + pa_sink_input_request_rewind(i, 0, FALSE, FALSE); } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) { /* OK, we're being uncorked. Make sure we're not rewound when * the hw buffer is remixed and request a remix. */ - i->thread_info.ignore_rewind = TRUE; - i->thread_info.since_underrun = 0; - pa_sink_request_rewind(i->sink, 0); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE); } + if (i->state_change) + i->state_change(i, state); + i->thread_info.state = state; } @@ -1017,17 +1041,17 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t pa_sink_input *i = PA_SINK_INPUT(o); pa_sink_input_assert_ref(i); - pa_assert(PA_SINK_INPUT_LINKED(i->thread_info.state)); + pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); switch (code) { case PA_SINK_INPUT_MESSAGE_SET_VOLUME: i->thread_info.volume = *((pa_cvolume*) userdata); - pa_sink_input_request_rewind(i, 0, FALSE); + pa_sink_input_request_rewind(i, 0, FALSE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_SET_MUTE: i->thread_info.muted = PA_PTR_TO_UINT(userdata); - pa_sink_input_request_rewind(i, 0, FALSE); + pa_sink_input_request_rewind(i, 0, FALSE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { @@ -1048,22 +1072,20 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t case PA_SINK_INPUT_MESSAGE_SET_STATE: { pa_sink_input *ssync; - set_state(i, PA_PTR_TO_UINT(userdata)); + pa_sink_input_set_state_within_thread(i, PA_PTR_TO_UINT(userdata)); for (ssync = i->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev) - set_state(ssync, PA_PTR_TO_UINT(userdata)); + pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata)); for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next) - set_state(ssync, PA_PTR_TO_UINT(userdata)); + pa_sink_input_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata)); return 0; } case PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: - i->thread_info.requested_sink_latency = (pa_usec_t) offset; - pa_sink_invalidate_requested_latency(i->sink); - + pa_sink_input_set_requested_latency_within_thread(i, (pa_usec_t) offset); return 0; } @@ -1088,8 +1110,8 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { return TRUE; } -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns) { - size_t l, lbq; +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns, pa_bool_t not_here) { + size_t lbq; pa_sink_input_assert_ref(i); @@ -1097,9 +1119,16 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam if (i->state == PA_SINK_INPUT_CORKED) return; - lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); + /* Calculate how much we can rewind locally without having to + * touch the sink */ + if (not_here) + lbq = 0; + else + lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); + /* Check if rewinding for the maximum is requested, and if so, fix up */ if (nbytes <= 0) { + /* Calulate maximum number of bytes that could be rewound in theory */ nbytes = i->sink->thread_info.max_rewind + lbq; @@ -1110,26 +1139,33 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam nbytes; } - /* Increase the number of bytes to rewrite, never decrease */ - if (nbytes > i->thread_info.rewrite_nbytes) - i->thread_info.rewrite_nbytes = nbytes; + if (not_here) { + i->thread_info.playing_for = 0; + i->thread_info.underrun_for = (uint64_t) -1; + } else { + /* Increase the number of bytes to rewrite, never decrease */ + if (nbytes < i->thread_info.rewrite_nbytes) + nbytes = i->thread_info.rewrite_nbytes; - if (!ignore_underruns) { /* Make sure to not overwrite over underruns */ - if ((int64_t) i->thread_info.rewrite_nbytes > i->thread_info.since_underrun) - i->thread_info.rewrite_nbytes = (size_t) i->thread_info.since_underrun; + if (!ignore_underruns) + if ((int64_t) nbytes > i->thread_info.playing_for) + nbytes = (size_t) i->thread_info.playing_for; + + i->thread_info.rewrite_nbytes = nbytes; } /* Transform to sink domain */ - l = i->thread_info.resampler ? - pa_resampler_result(i->thread_info.resampler, i->thread_info.rewrite_nbytes) : - i->thread_info.rewrite_nbytes; + nbytes = + i->thread_info.resampler ? + pa_resampler_result(i->thread_info.resampler, nbytes) : + nbytes; - if (l <= 0) + if (nbytes <= 0) return; - if (l > lbq) - pa_sink_request_rewind(i->sink, l - lbq); + if (nbytes > lbq) + pa_sink_request_rewind(i->sink, nbytes - lbq); } pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) { diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index b433edc0..b70cb0ac 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -46,7 +46,7 @@ typedef enum pa_sink_input_state { PA_SINK_INPUT_UNLINKED /*< The stream is dead */ } pa_sink_input_state_t; -static inline pa_bool_t PA_SINK_INPUT_LINKED(pa_sink_input_state_t x) { +static inline pa_bool_t PA_SINK_INPUT_IS_LINKED(pa_sink_input_state_t x) { return x == PA_SINK_INPUT_DRAINED || x == PA_SINK_INPUT_RUNNING || x == PA_SINK_INPUT_CORKED; } @@ -106,7 +106,7 @@ struct pa_sink_input { void (*process_rewind) (pa_sink_input *i, size_t nbytes); /* may NOT be NULL */ /* Called whenever the maximum rewindable size of the sink - * changes. Called from RT context. */ + * changes. Called from IO context. */ void (*update_max_rewind) (pa_sink_input *i, size_t nbytes); /* may be NULL */ /* If non-NULL this function is called when the input is first @@ -138,6 +138,10 @@ struct pa_sink_input { instead. */ pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */ + /* If non_NULL this function is called from thread context if the + * state changes. The old state is found in thread_info.state. */ + void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */ + struct { pa_sink_input_state_t state; pa_atomic_t drained, render_memblockq_is_empty; @@ -152,7 +156,7 @@ struct pa_sink_input { pa_memblockq *render_memblockq; size_t rewrite_nbytes; - int64_t since_underrun; + uint64_t underrun_for, playing_for; pa_bool_t ignore_rewind; pa_sink_input *sync_prev, *sync_next; @@ -237,7 +241,7 @@ fully -- or at all. If the request for a rewrite was successful, the sink driver will call ->rewind() and pass the number of bytes that could be rewound in the HW device. This functionality is required for implementing the "zero latency" write-through functionality. */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind); +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind, pa_bool_t not_here); /* Callable by everyone from main thread*/ @@ -257,7 +261,7 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, int immediately); +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately); pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i); @@ -269,8 +273,12 @@ void pa_sink_input_drop(pa_sink_input *i, size_t length); void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */); +void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state); + int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); +pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec); + typedef struct pa_sink_input_move_info { pa_sink_input *sink_input; pa_sink_input *ghost_sink_input; diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 452dab79..a2a02ebf 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -265,8 +265,8 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { return 0; suspend_change = - (s->state == PA_SINK_SUSPENDED && PA_SINK_OPENED(state)) || - (PA_SINK_OPENED(s->state) && state == PA_SINK_SUSPENDED); + (s->state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(state)) || + (PA_SINK_IS_OPENED(s->state) && state == PA_SINK_SUSPENDED); if (s->set_state) if ((ret = s->set_state(s, state)) < 0) @@ -328,7 +328,7 @@ void pa_sink_unlink(pa_sink* s) { * may be called multiple times on the same sink without bad * effects. */ - linked = PA_SINK_LINKED(s->state); + linked = PA_SINK_IS_LINKED(s->state); if (linked) pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK], s); @@ -366,7 +366,7 @@ static void sink_free(pa_object *o) { pa_assert(s); pa_assert(pa_sink_refcnt(s) == 0); - if (PA_SINK_LINKED(s->state)) + if (PA_SINK_IS_LINKED(s->state)) pa_sink_unlink(s); pa_log_info("Freeing sink %u \"%s\"", s->index, s->name); @@ -397,7 +397,6 @@ static void sink_free(pa_object *o) { void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) { pa_sink_assert_ref(s); - pa_assert(q); s->asyncmsgq = q; @@ -407,7 +406,6 @@ void pa_sink_set_asyncmsgq(pa_sink *s, pa_asyncmsgq *q) { void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { pa_sink_assert_ref(s); - pa_assert(p); s->rtpoll = p; if (s->monitor_source) @@ -416,7 +414,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) { int pa_sink_update_status(pa_sink*s) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); if (s->state == PA_SINK_SUSPENDED) return 0; @@ -426,7 +424,7 @@ int pa_sink_update_status(pa_sink*s) { int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); if (suspend) return sink_set_state(s, PA_SINK_SUSPENDED); @@ -438,7 +436,10 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_sink_input *i; void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); + + /* Make sure the sink code already reset the counter! */ + pa_assert(s->thread_info.rewind_nbytes <= 0); if (nbytes <= 0) return; @@ -450,8 +451,9 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_sink_input_process_rewind(i, nbytes); } - if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) pa_source_process_rewind(s->monitor_source, nbytes); + } static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, unsigned maxinfo) { @@ -557,7 +559,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { size_t block_size_max; pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); pa_assert(pa_frame_aligned(length, &s->sample_spec)); pa_assert(result); @@ -621,7 +623,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { if (s->thread_info.state == PA_SINK_RUNNING) inputs_drop(s, info, n, result->length); - if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) pa_source_post(s->monitor_source, result); pa_sink_unref(s); @@ -633,7 +635,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { size_t length, block_size_max; pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); pa_assert(target); pa_assert(target->memblock); pa_assert(target->length > 0); @@ -700,7 +702,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { if (s->thread_info.state == PA_SINK_RUNNING) inputs_drop(s, info, n, target->length); - if (s->monitor_source && PA_SOURCE_OPENED(pa_source_get_state(s->monitor_source))) + if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) pa_source_post(s->monitor_source, target); pa_sink_unref(s); @@ -711,7 +713,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { size_t l, d; pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); pa_assert(target); pa_assert(target->memblock); pa_assert(target->length > 0); @@ -739,7 +741,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) { void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); + pa_assert(PA_SINK_IS_OPENED(s->thread_info.state)); pa_assert(length > 0); pa_assert(pa_frame_aligned(length, &s->sample_spec)); pa_assert(result); @@ -755,50 +757,15 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_sink_render_into_full(s, result); } -void pa_sink_skip(pa_sink *s, size_t length) { - pa_sink_input *i; - void *state = NULL; - - pa_sink_assert_ref(s); - pa_assert(PA_SINK_OPENED(s->thread_info.state)); - pa_assert(length > 0); - pa_assert(pa_frame_aligned(length, &s->sample_spec)); - - s->thread_info.rewind_nbytes = 0; - - if (pa_source_used_by(s->monitor_source)) { - pa_memchunk chunk; - - /* If something is connected to our monitor source, we have to - * pass valid data to it */ - - while (length > 0) { - pa_sink_render(s, length, &chunk); - pa_memblock_unref(chunk.memblock); - - pa_assert(chunk.length <= length); - length -= chunk.length; - } - - } else { - /* Ok, noone cares about the rendered data, so let's not even render it */ - - while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) { - pa_sink_input_assert_ref(i); - pa_sink_input_drop(i, length); - } - } -} - pa_usec_t pa_sink_get_latency(pa_sink *s) { pa_usec_t usec = 0; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); /* The returned value is supposed to be in the time domain of the sound card! */ - if (!PA_SINK_OPENED(s->state)) + if (!PA_SINK_IS_OPENED(s->state)) return 0; if (s->get_latency) @@ -814,7 +781,7 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) { int changed; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); pa_assert(volume); changed = !pa_cvolume_equal(volume, &s->volume); @@ -834,7 +801,7 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s) { struct pa_cvolume old_volume; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); old_volume = s->volume; @@ -854,7 +821,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) { int changed; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); changed = s->muted != mute; s->muted = mute; @@ -873,7 +840,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *s) { pa_bool_t old_muted; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); old_muted = s->muted; @@ -914,7 +881,7 @@ void pa_sink_set_description(pa_sink *s, const char *description) { pa_xfree(n); } - if (PA_SINK_LINKED(s->state)) { + if (PA_SINK_IS_LINKED(s->state)) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s); } @@ -924,7 +891,7 @@ unsigned pa_sink_linked_by(pa_sink *s) { unsigned ret; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); ret = pa_idxset_size(s->inputs); @@ -941,7 +908,7 @@ unsigned pa_sink_used_by(pa_sink *s) { unsigned ret; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); ret = pa_idxset_size(s->inputs); pa_assert(ret >= s->n_corked); @@ -980,24 +947,26 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse i->thread_info.sync_next->thread_info.sync_prev = i; } - pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); - pa_assert(!i->thread_info.attached); i->thread_info.attached = TRUE; if (i->attach) i->attach(i); - /* If you change anything here, make sure to change the - * ghost sink input handling a few lines down at - * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + pa_sink_input_set_state_within_thread(i, i->state); + + pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); pa_sink_invalidate_requested_latency(s); - /* Make sure we're not rewound when the hw buffer is remixed and request a remix*/ - i->thread_info.ignore_rewind = TRUE; - i->thread_info.since_underrun = 0; - pa_sink_request_rewind(s, 0); + /* We don't rewind here automatically. This is left to the + * sink input implementor because some sink inputs need a + * slow start, i.e. need some time to buffer client + * samples before beginning streaming. */ + + /* If you change anything here, make sure to change the + * ghost sink input handling a few lines down at + * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ return 0; } @@ -1009,6 +978,8 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse * sink input handling a few lines down at * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + pa_sink_input_set_state_within_thread(i, i->state); + if (i->detach) i->detach(i); @@ -1036,7 +1007,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse pa_sink_input_unref(i); pa_sink_invalidate_requested_latency(s); - pa_sink_request_rewind(s, 0); return 0; @@ -1117,11 +1087,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse if (info->ghost_sink_input->attach) info->ghost_sink_input->attach(info->ghost_sink_input); - } pa_sink_invalidate_requested_latency(s); - pa_sink_request_rewind(s, 0); return 0; @@ -1196,14 +1164,14 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend) { void pa_sink_detach(pa_sink *s) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_DETACH, NULL, 0, NULL); } void pa_sink_attach(pa_sink *s) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_ATTACH, NULL, 0, NULL); } @@ -1213,7 +1181,7 @@ void pa_sink_detach_within_thread(pa_sink *s) { void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) if (i->detach) @@ -1228,7 +1196,7 @@ void pa_sink_attach_within_thread(pa_sink *s) { void *state = NULL; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL))) if (i->attach) @@ -1240,7 +1208,7 @@ void pa_sink_attach_within_thread(pa_sink *s) { void pa_sink_request_rewind(pa_sink*s, size_t nbytes) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); if (nbytes <= 0) nbytes = s->thread_info.max_rewind; @@ -1290,9 +1258,9 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) { pa_usec_t usec = 0; pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->state)); + pa_assert(PA_SINK_IS_LINKED(s->state)); - if (!PA_SINK_OPENED(s->state)) + if (!PA_SINK_IS_OPENED(s->state)) return 0; if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) @@ -1325,7 +1293,7 @@ void pa_sink_set_max_rewind(pa_sink *s, size_t max_rewind) { void pa_sink_invalidate_requested_latency(pa_sink *s) { pa_sink_assert_ref(s); - pa_assert(PA_SINK_LINKED(s->thread_info.state)); + pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); if (!s->thread_info.requested_latency_valid) return; diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 7bc4a706..f25f48cf 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -33,7 +33,6 @@ typedef struct pa_sink pa_sink; #include #include -#include #include #include #include @@ -52,11 +51,11 @@ typedef enum pa_sink_state { PA_SINK_UNLINKED } pa_sink_state_t; -static inline pa_bool_t PA_SINK_OPENED(pa_sink_state_t x) { +static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) { return x == PA_SINK_RUNNING || x == PA_SINK_IDLE; } -static inline pa_bool_t PA_SINK_LINKED(pa_sink_state_t x) { +static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) { return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED; } @@ -94,13 +93,42 @@ struct pa_sink { pa_usec_t min_latency; /* we won't go below this latency */ pa_usec_t max_latency; /* An upper limit for the latencies */ + /* Called when the main loop requests a state change. Called from + * main loop context. If returns -1 the state change will be + * inhibited */ int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */ - int (*get_volume)(pa_sink *s); /* dito */ + + /* Callled when the volume is queried. Called from main loop + * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message + * will be sent to the IO thread instead. */ + int (*get_volume)(pa_sink *s); /* may be null */ + + /* Called when the volume shall be changed. Called from main loop + * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message + * will be sent to the IO thread instead. */ int (*set_volume)(pa_sink *s); /* dito */ + + /* Called when the mute setting is queried. Called from main loop + * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message + * will be sent to the IO thread instead. */ int (*get_mute)(pa_sink *s); /* dito */ + + /* Called when the mute setting shall be changed. Called from main + * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE + * message will be sent to the IO thread instead. */ int (*set_mute)(pa_sink *s); /* dito */ - pa_usec_t (*get_latency)(pa_sink *s); /* dito */ + + /* Called when the latency is queried. Called from main loop + context. If this is NULL a PA_SINK_MESSAGE_GET_LATENCY message + will be sent to the IO thread instead. */ + pa_usec_t (*get_latency)(pa_sink *s); /* dito */ + + /* Called when a rewind request is issued. Called from IO thread + * context. */ void (*request_rewind)(pa_sink *s); /* dito */ + + /* Called when a the requested latency is changed. Called from IO + * thread context. */ void (*update_requested_latency)(pa_sink *s); /* dito */ /* Contains copies of the above data so that the real-time worker @@ -213,7 +241,6 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result); void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result); void pa_sink_render_into(pa_sink*s, pa_memchunk *target); void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target); -void pa_sink_skip(pa_sink *s, size_t length); void pa_sink_process_rewind(pa_sink *s, size_t nbytes); diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 604723f1..918313f8 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -55,6 +55,8 @@ typedef struct file_stream { SNDFILE *sndfile; sf_count_t (*readf_function)(SNDFILE *sndfile, void *ptr, sf_count_t frames); + /* We need this memblockq here to easily fulfill rewind requests + * (even beyond the file start!) */ pa_memblockq *memblockq; } file_stream; @@ -66,6 +68,7 @@ PA_DECLARE_CLASS(file_stream); #define FILE_STREAM(o) (file_stream_cast(o)) static PA_DEFINE_CHECK_TYPE(file_stream, pa_msgobject); +/* Called from main context */ static void file_stream_unlink(file_stream *u) { pa_assert(u); @@ -80,6 +83,7 @@ static void file_stream_unlink(file_stream *u) { file_stream_unref(u); } +/* Called from main context */ static void file_stream_free(pa_object *o) { file_stream *u = FILE_STREAM(o); pa_assert(u); @@ -93,6 +97,7 @@ static void file_stream_free(pa_object *o) { pa_xfree(u); } +/* Called from main context */ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { file_stream *u = FILE_STREAM(o); file_stream_assert_ref(u); @@ -106,6 +111,7 @@ static int file_stream_process_msg(pa_msgobject *o, int code, void*userdata, int return 0; } +/* Called from main context */ static void sink_input_kill_cb(pa_sink_input *i) { file_stream *u; @@ -116,6 +122,22 @@ static void sink_input_kill_cb(pa_sink_input *i) { file_stream_unlink(u); } +/* Called from IO thread context */ +static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { + file_stream *u; + + pa_sink_input_assert_ref(i); + u = FILE_STREAM(i->userdata); + file_stream_assert_ref(u); + + /* If we are added for the first time, ask for a rewinding so that + * we are heard right-away. */ + if (PA_SINK_INPUT_IS_LINKED(state) && + i->thread_info.state == PA_SINK_INPUT_INIT) + pa_sink_input_request_rewind(i, 0, FALSE, TRUE); +} + +/* Called from IO thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { file_stream *u; @@ -131,6 +153,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk for (;;) { pa_memchunk tchunk; + size_t fs; + void *p; + sf_count_t n; if (pa_memblockq_peek(u->memblockq, chunk) >= 0) { pa_memblockq_drop(u->memblockq, chunk->length); @@ -143,36 +168,19 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk tchunk.memblock = pa_memblock_new(i->sink->core->mempool, length); tchunk.index = 0; - if (u->readf_function) { - sf_count_t n; - void *p; - size_t fs = pa_frame_size(&i->sample_spec); + p = pa_memblock_acquire(tchunk.memblock); - p = pa_memblock_acquire(tchunk.memblock); + if (u->readf_function) { + fs = pa_frame_size(&i->sample_spec); n = u->readf_function(u->sndfile, p, length/fs); - pa_memblock_release(tchunk.memblock); - - if (n <= 0) - n = 0; - - tchunk.length = n * fs; - } else { - sf_count_t n; - void *p; - - p = pa_memblock_acquire(tchunk.memblock); + fs = 1; n = sf_read_raw(u->sndfile, p, length); - pa_memblock_release(tchunk.memblock); - - if (n <= 0) - n = 0; - - tchunk.length = n; } - if (tchunk.length <= 0) { + pa_memblock_release(tchunk.memblock); + if (n <= 0) { pa_memblock_unref(tchunk.memblock); sf_close(u->sndfile); @@ -180,6 +188,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk break; } + tchunk.length = n * fs; + pa_memblockq_push(u->memblockq, &tchunk); pa_memblock_unref(tchunk.memblock); } @@ -196,7 +206,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk } return -1; -} + } static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { file_stream *u; @@ -334,6 +344,7 @@ int pa_play_file( u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; + u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; pa_sink_input_get_silence(u->sink_input, &silence); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index de543a57..7f5f374e 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -88,6 +88,7 @@ static void reset_callbacks(pa_source_output *o) { o->moved = NULL; o->kill = NULL; o->get_latency = NULL; + o->state_change = NULL; } pa_source_output* pa_source_output_new( @@ -263,7 +264,7 @@ void pa_source_output_unlink(pa_source_output*o) { pa_source_output_ref(o); - linked = PA_SOURCE_OUTPUT_LINKED(o->state); + linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state); if (linked) pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); @@ -295,7 +296,7 @@ static void source_output_free(pa_object* mo) { pa_assert(pa_source_output_refcnt(o) == 0); - if (PA_SOURCE_OUTPUT_LINKED(o->state)) + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) pa_source_output_unlink(o); pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME))); @@ -335,7 +336,7 @@ void pa_source_output_put(pa_source_output *o) { void pa_source_output_kill(pa_source_output*o) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); if (o->kill) o->kill(o); @@ -345,7 +346,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o) { pa_usec_t r = 0; pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); if (pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, &r, 0, NULL) < 0) r = 0; @@ -362,7 +363,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { size_t limit, mbs = 0; pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(chunk); pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec)); @@ -419,7 +420,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in sink sample spec */) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); if (nbytes <= 0) @@ -446,28 +447,48 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in si /* Called from thread context */ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); if (o->update_max_rewind) o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes); } -pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { - pa_source_output_assert_ref(o); +static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) { + pa_source_assert_ref(s); - if (usec != (pa_usec_t) -1) { + if (usec == (pa_usec_t) -1) + return usec; - if (o->source->max_latency > 0 && usec > o->source->max_latency) - usec = o->source->max_latency; + if (s->max_latency > 0 && usec > s->max_latency) + usec = s->max_latency; - if (o->source->min_latency > 0 && usec < o->source->min_latency) - usec = o->source->min_latency; - } + if (s->min_latency > 0 && usec < s->min_latency) + usec = s->min_latency; + + return usec; +} + +pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) { - if (PA_SOURCE_OUTPUT_LINKED(o->state)) + usec = fixup_latency(o->source, usec); + + o->thread_info.requested_source_latency = usec; + pa_source_invalidate_requested_latency(o->source); + + return usec; +} + +pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { + pa_source_output_assert_ref(o); + + usec = fixup_latency(o->source, usec); + + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, (int64_t) usec, NULL, NULL); else { + /* If this sink input is not realized yet, we have to touch + * the thread info data directly */ o->thread_info.requested_source_latency = usec; o->source->thread_info.requested_latency_valid = FALSE; } @@ -477,14 +498,14 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING); } int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_return_val_if_fail(o->thread_info.resampler, -1); if (o->sample_spec.rate == rate) @@ -515,7 +536,7 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) { else pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME); - if (PA_SOURCE_OUTPUT_LINKED(o->state)) { + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } @@ -533,7 +554,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_source_output_move_hook_data hook_data; pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_source_assert_ref(dest); origin = o->source; @@ -616,12 +637,21 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { return 0; } +void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) { + pa_source_output_assert_ref(o); + + if (o->state_change) + o->state_change(o, state); + + o->thread_info.state = state; +} + /* Called from thread context */ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk* chunk) { pa_source_output *o = PA_SOURCE_OUTPUT(mo); pa_source_output_assert_ref(o); - pa_assert(PA_SOURCE_OUTPUT_LINKED(o->thread_info.state)); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); switch (code) { @@ -633,25 +663,20 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int return 0; } - case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: { + case PA_SOURCE_OUTPUT_MESSAGE_SET_RATE: o->thread_info.sample_spec.rate = PA_PTR_TO_UINT(userdata); pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata)); - return 0; - } - case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: { - o->thread_info.state = PA_PTR_TO_UINT(userdata); + case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: + pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata)); return 0; - } case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: - o->thread_info.requested_source_latency = (pa_usec_t) offset; - pa_source_invalidate_requested_latency(o->source); - + pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset); return 0; } diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index e7d8963f..67cb3761 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -42,7 +42,7 @@ typedef enum pa_source_output_state { PA_SOURCE_OUTPUT_UNLINKED } pa_source_output_state_t; -static inline pa_bool_t PA_SOURCE_OUTPUT_LINKED(pa_source_output_state_t x) { +static inline pa_bool_t PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_state_t x) { return x == PA_SOURCE_OUTPUT_RUNNING || x == PA_SOURCE_OUTPUT_CORKED; } @@ -83,11 +83,11 @@ struct pa_source_output { void (*push)(pa_source_output *o, const pa_memchunk *chunk); /* Only relevant for monitor sources right now: called when the - * recorded stream is rewound. */ + * recorded stream is rewound. Called from IO context*/ void (*process_rewind)(pa_source_output *o, size_t nbytes); /* Called whenever the maximum rewindable size of the source - * changes. Called from RT context. */ + * changes. Called from IO thread context. */ void (*update_max_rewind) (pa_source_output *o, size_t nbytes); /* may be NULL */ /* If non-NULL this function is called when the output is first @@ -116,6 +116,10 @@ struct pa_source_output { thread instead. */ pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */ + /* If non_NULL this function is called from thread context if the + * state changes. The old state is found in thread_info.state. */ + void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */ + struct { pa_source_output_state_t state; @@ -213,4 +217,8 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes); int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk); +void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state); + +pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec); + #endif diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index dab307e9..4a2173ca 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -228,8 +228,8 @@ static int source_set_state(pa_source *s, pa_source_state_t state) { return 0; suspend_change = - (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_OPENED(state)) || - (PA_SOURCE_OPENED(s->state) && state == PA_SOURCE_SUSPENDED); + (s->state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(state)) || + (PA_SOURCE_IS_OPENED(s->state) && state == PA_SOURCE_SUSPENDED); if (s->set_state) if ((ret = s->set_state(s, state)) < 0) @@ -284,7 +284,7 @@ void pa_source_unlink(pa_source *s) { /* See pa_sink_unlink() for a couple of comments how this function * works. */ - linked = PA_SOURCE_LINKED(s->state); + linked = PA_SOURCE_IS_LINKED(s->state); if (linked) pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], s); @@ -319,7 +319,7 @@ static void source_free(pa_object *o) { pa_assert(s); pa_assert(pa_source_refcnt(s) == 0); - if (PA_SOURCE_LINKED(s->state)) + if (PA_SOURCE_IS_LINKED(s->state)) pa_source_unlink(s); pa_log_info("Freeing source %u \"%s\"", s->index, s->name); @@ -345,21 +345,19 @@ static void source_free(pa_object *o) { void pa_source_set_asyncmsgq(pa_source *s, pa_asyncmsgq *q) { pa_source_assert_ref(s); - pa_assert(q); s->asyncmsgq = q; } void pa_source_set_rtpoll(pa_source *s, pa_rtpoll *p) { pa_source_assert_ref(s); - pa_assert(p); s->rtpoll = p; } int pa_source_update_status(pa_source*s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); if (s->state == PA_SOURCE_SUSPENDED) return 0; @@ -369,7 +367,7 @@ int pa_source_update_status(pa_source*s) { int pa_source_suspend(pa_source *s, pa_bool_t suspend) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); if (suspend) return source_set_state(s, PA_SOURCE_SUSPENDED); @@ -382,7 +380,7 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) { void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_OPENED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); if (nbytes <= 0) return; @@ -400,7 +398,7 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_OPENED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state)); pa_assert(chunk); if (s->thread_info.state != PA_SOURCE_RUNNING) @@ -436,9 +434,9 @@ pa_usec_t pa_source_get_latency(pa_source *s) { pa_usec_t usec; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); - if (!PA_SOURCE_OPENED(s->state)) + if (!PA_SOURCE_IS_OPENED(s->state)) return 0; if (s->get_latency) @@ -454,7 +452,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) { int changed; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_assert(volume); changed = !pa_cvolume_equal(volume, &s->volume); @@ -474,7 +472,7 @@ const pa_cvolume *pa_source_get_volume(pa_source *s) { pa_cvolume old_volume; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); old_volume = s->volume; @@ -494,7 +492,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) { int changed; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); changed = s->muted != mute; s->muted = mute; @@ -513,7 +511,7 @@ pa_bool_t pa_source_get_mute(pa_source *s) { pa_bool_t old_muted; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); old_muted = s->muted; @@ -546,7 +544,7 @@ void pa_source_set_description(pa_source *s, const char *description) { else pa_proplist_unset(s->proplist, PA_PROP_DEVICE_DESCRIPTION); - if (PA_SOURCE_LINKED(s->state)) { + if (PA_SOURCE_IS_LINKED(s->state)) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s); } @@ -554,7 +552,7 @@ void pa_source_set_description(pa_source *s, const char *description) { unsigned pa_source_linked_by(pa_source *s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); return pa_idxset_size(s->outputs); } @@ -563,7 +561,7 @@ unsigned pa_source_used_by(pa_source *s) { unsigned ret; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); ret = pa_idxset_size(s->outputs); pa_assert(ret >= s->n_corked); @@ -590,6 +588,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ if (o->attach) o->attach(o); + pa_source_output_set_state_within_thread(o, o->state); + pa_source_invalidate_requested_latency(s); return 0; @@ -598,6 +598,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: { pa_source_output *o = PA_SOURCE_OUTPUT(userdata); + pa_source_output_set_state_within_thread(o, o->state); + if (o->detach) o->detach(o); @@ -676,14 +678,14 @@ int pa_source_suspend_all(pa_core *c, pa_bool_t suspend) { void pa_source_detach(pa_source *s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_DETACH, NULL, 0, NULL); } void pa_source_attach(pa_source *s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_ATTACH, NULL, 0, NULL); } @@ -693,7 +695,7 @@ void pa_source_detach_within_thread(pa_source *s) { void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state)); while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) if (o->detach) @@ -705,7 +707,7 @@ void pa_source_attach_within_thread(pa_source *s) { void *state = NULL; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state)); while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL))) if (o->attach) @@ -746,9 +748,9 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) { pa_usec_t usec; pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->state)); + pa_assert(PA_SOURCE_IS_LINKED(s->state)); - if (!PA_SOURCE_OPENED(s->state)) + if (!PA_SOURCE_IS_OPENED(s->state)) return 0; if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) < 0) @@ -778,7 +780,7 @@ void pa_source_set_max_rewind(pa_source *s, size_t max_rewind) { void pa_source_invalidate_requested_latency(pa_source *s) { pa_source_assert_ref(s); - pa_assert(PA_SOURCE_LINKED(s->thread_info.state)); + pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state)); if (!s->thread_info.requested_latency_valid) return; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index b8859c84..cce54620 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -33,7 +33,6 @@ typedef struct pa_source pa_source; #include #include -#include #include #include #include @@ -54,11 +53,11 @@ typedef enum pa_source_state { PA_SOURCE_UNLINKED } pa_source_state_t; -static inline pa_bool_t PA_SOURCE_OPENED(pa_source_state_t x) { +static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) { return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE; } -static inline pa_bool_t PA_SOURCE_LINKED(pa_source_state_t x) { +static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) { return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED; } diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c index daa6a96e..dff9af9d 100644 --- a/src/utils/pacmd.c +++ b/src/utils/pacmd.c @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -49,6 +50,7 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) { char ibuf[256], obuf[256]; size_t ibuf_index, ibuf_length, obuf_index, obuf_length; fd_set ifds, ofds; + char *cli; if (pa_pid_file_check_running(&pid, "pulseaudio") < 0) { pa_log("no PulseAudio daemon running"); @@ -62,7 +64,10 @@ int main(PA_GCC_UNUSED int argc, PA_GCC_UNUSED char*argv[]) { memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; - pa_runtime_path("cli", sa.sun_path, sizeof(sa.sun_path)); + + cli = pa_runtime_path("cli"); + pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path)); + pa_xfree(cli); for (i = 0; i < 5; i++) { int r; -- cgit From 49b1b15ef076e36aa7dc62eb664f848b6a5ca531 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 May 2008 01:20:46 +0000 Subject: don't enable prebuffering if we just call is_readable() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2341 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/memblockq.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index 947c69a2..13eb1101 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -428,6 +428,15 @@ finish: pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq) { pa_assert(bq); + if (bq->in_prebuf) + return pa_memblockq_get_length(bq) < bq->prebuf; + else + return bq->prebuf > 0 && bq->read_index >= bq->write_index; +} + +static pa_bool_t update_prebuf(pa_memblockq *bq) { + pa_assert(bq); + if (bq->in_prebuf) { if (pa_memblockq_get_length(bq) < bq->prebuf) @@ -452,7 +461,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) { pa_assert(chunk); /* We need to pre-buffer */ - if (pa_memblockq_prebuf_active(bq)) + if (update_prebuf(bq)) return -1; fix_current_read(bq); @@ -515,7 +524,7 @@ void pa_memblockq_drop(pa_memblockq *bq, size_t length) { while (length > 0) { /* Do not drop any data when we are in prebuffering mode */ - if (pa_memblockq_prebuf_active(bq)) + if (update_prebuf(bq)) break; fix_current_read(bq); -- cgit From 7d6269e57a529b141d179650f42be58d54f6059a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 May 2008 01:21:22 +0000 Subject: add multiarch paths to default LADSPA search path git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2342 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index 5cc9546f..799e7b26 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1311,7 +1311,7 @@ module_remap_sink_la_LDFLAGS = -module -avoid-version module_remap_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la module_ladspa_sink_la_SOURCES = modules/module-ladspa-sink.c modules/ladspa.h -module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa\" $(AM_CFLAGS) +module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa\" $(AM_CFLAGS) module_ladspa_sink_la_LDFLAGS = -module -avoid-version module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore.la -- cgit From 775bc6c108cc1070bf97758036b5d9ea745e05ae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 May 2008 01:23:32 +0000 Subject: some modernizations git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2343 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/ioline.c | 35 ++++++++++++++++++++--------------- src/pulsecore/ioline.h | 4 +++- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c index 6d11e4fc..85bbadc4 100644 --- a/src/pulsecore/ioline.c +++ b/src/pulsecore/ioline.c @@ -49,7 +49,6 @@ struct pa_ioline { pa_iochannel *io; pa_defer_event *defer_event; pa_mainloop_api *mainloop; - int dead; char *wbuf; size_t wbuf_length, wbuf_index, wbuf_valid_length; @@ -57,10 +56,11 @@ struct pa_ioline { char *rbuf; size_t rbuf_length, rbuf_index, rbuf_valid_length; - void (*callback)(pa_ioline*io, const char *s, void *userdata); + pa_ioline_cb_t callback; void *userdata; - int defer_close; + pa_bool_t dead:1; + pa_bool_t defer_close:1; }; static void io_callback(pa_iochannel*io, void *userdata); @@ -73,7 +73,6 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) { l = pa_xnew(pa_ioline, 1); PA_REFCNT_INIT(l); l->io = io; - l->dead = 0; l->wbuf = NULL; l->wbuf_length = l->wbuf_index = l->wbuf_valid_length = 0; @@ -89,7 +88,8 @@ pa_ioline* pa_ioline_new(pa_iochannel *io) { l->defer_event = l->mainloop->defer_new(l->mainloop, defer_callback, l); l->mainloop->defer_enable(l->defer_event, 0); - l->defer_close = 0; + l->dead = FALSE; + l->defer_close = FALSE; pa_iochannel_set_callback(io, io_callback, l); @@ -130,7 +130,7 @@ void pa_ioline_close(pa_ioline *l) { pa_assert(l); pa_assert(PA_REFCNT_VALUE(l) >= 1); - l->dead = 1; + l->dead = TRUE; if (l->io) { pa_iochannel_free(l->io); @@ -166,11 +166,13 @@ void pa_ioline_puts(pa_ioline *l, const char *c) { /* In case the allocated buffer is too small, enlarge it. */ if (l->wbuf_valid_length + len > l->wbuf_length) { size_t n = l->wbuf_valid_length+len; - char *new = pa_xmalloc(n); + char *new = pa_xnew(char, n); + if (l->wbuf) { memcpy(new, l->wbuf+l->wbuf_index, l->wbuf_valid_length); pa_xfree(l->wbuf); } + l->wbuf = new; l->wbuf_length = n; l->wbuf_index = 0; @@ -191,15 +193,18 @@ void pa_ioline_puts(pa_ioline *l, const char *c) { } } -void pa_ioline_set_callback(pa_ioline*l, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata) { +void pa_ioline_set_callback(pa_ioline*l, pa_ioline_cb_t callback, void *userdata) { pa_assert(l); pa_assert(PA_REFCNT_VALUE(l) >= 1); + if (l->dead) + return; + l->callback = callback; l->userdata = userdata; } -static void failure(pa_ioline *l, int process_leftover) { +static void failure(pa_ioline *l, pa_bool_t process_leftover) { pa_assert(l); pa_assert(PA_REFCNT_VALUE(l) >= 1); pa_assert(!l->dead); @@ -282,7 +287,7 @@ static int do_read(pa_ioline *l) { memmove(l->rbuf, l->rbuf+l->rbuf_index, l->rbuf_valid_length); } else { /* Enlarge the buffer */ - char *new = pa_xmalloc(n); + char *new = pa_xnew(char, n); if (l->rbuf_valid_length) memcpy(new, l->rbuf+l->rbuf_index, l->rbuf_valid_length); pa_xfree(l->rbuf); @@ -305,9 +310,9 @@ static int do_read(pa_ioline *l) { if (r < 0 && errno != ECONNRESET) { pa_log("read(): %s", pa_cstrerror(errno)); - failure(l, 0); + failure(l, FALSE); } else - failure(l, 1); + failure(l, TRUE); return -1; } @@ -338,7 +343,7 @@ static int do_write(pa_ioline *l) { if (r < 0 && errno != EPIPE) pa_log("write(): %s", pa_cstrerror(errno)); - failure(l, 0); + failure(l, FALSE); return -1; } @@ -370,7 +375,7 @@ static void do_work(pa_ioline *l) { do_write(l); if (l->defer_close && !l->wbuf_valid_length) - failure(l, 1); + failure(l, TRUE); pa_ioline_unref(l); } @@ -400,7 +405,7 @@ void pa_ioline_defer_close(pa_ioline *l) { pa_assert(l); pa_assert(PA_REFCNT_VALUE(l) >= 1); - l->defer_close = 1; + l->defer_close = TRUE; if (!l->wbuf_valid_length) l->mainloop->defer_enable(l->defer_event, 1); diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h index 8475b798..f4edc7b4 100644 --- a/src/pulsecore/ioline.h +++ b/src/pulsecore/ioline.h @@ -33,6 +33,8 @@ typedef struct pa_ioline pa_ioline; +typedef void (*pa_ioline_cb_t)(pa_ioline*io, const char *s, void *userdata); + pa_ioline* pa_ioline_new(pa_iochannel *io); void pa_ioline_unref(pa_ioline *l); pa_ioline* pa_ioline_ref(pa_ioline *l); @@ -45,7 +47,7 @@ void pa_ioline_puts(pa_ioline *s, const char *c); void pa_ioline_printf(pa_ioline *s, const char *format, ...) PA_GCC_PRINTF_ATTR(2,3); /* Set the callback function that is called for every recieved line */ -void pa_ioline_set_callback(pa_ioline*io, void (*callback)(pa_ioline*io, const char *s, void *userdata), void *userdata); +void pa_ioline_set_callback(pa_ioline*io, pa_ioline_cb_t callback, void *userdata); /* Make sure to close the ioline object as soon as the send buffer is emptied */ void pa_ioline_defer_close(pa_ioline *io); -- cgit From 792ef5c244f2374d88975f042b2b5f3def99ce0e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 May 2008 01:24:01 +0000 Subject: fix a compiler warning git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2344 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 1da920a9..bb377c44 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1149,7 +1149,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam /* Make sure to not overwrite over underruns */ if (!ignore_underruns) - if ((int64_t) nbytes > i->thread_info.playing_for) + if (nbytes > i->thread_info.playing_for) nbytes = (size_t) i->thread_info.playing_for; i->thread_info.rewrite_nbytes = nbytes; -- cgit From 06b9140e109a591529ec7229085fe138ce79a6d1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 May 2008 01:24:47 +0000 Subject: reorderer a few things git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2345 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sample-util.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c index d295742f..a8028296 100644 --- a/src/pulsecore/sample-util.c +++ b/src/pulsecore/sample-util.c @@ -1002,20 +1002,19 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool, } void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const void *src, size_t sstr, unsigned n) { - const float *s; float *d; - if (format != PA_SAMPLE_FLOAT32BE && format != PA_SAMPLE_FLOAT32LE) - return; - - s = src; - d = dst; + s = src; d = dst; if (format == PA_SAMPLE_FLOAT32NE) { - const static float minus_one = -1.0, plus_one = 1.0; - oil_clip_f32(dst, dstr, src, sstr, n, &minus_one, &plus_one); - } else + + float minus_one = -1.0, plus_one = 1.0; + oil_clip_f32(d, dstr, s, sstr, n, &minus_one, &plus_one); + + } else { + pa_assert(format == PA_SAMPLE_FLOAT32RE); + for (; n > 0; n--) { float f; @@ -1026,4 +1025,5 @@ void pa_sample_clamp(pa_sample_format_t format, void *dst, size_t dstr, const vo s = (const float*) ((const uint8_t*) s + sstr); d = (float*) ((uint8_t*) d + dstr); } + } } -- cgit From bfb2691fac92a3a6f44ea309b6969e8ed96b7711 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 May 2008 01:25:22 +0000 Subject: a few modernizations git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2346 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/socket-client.c | 50 +++++++++++++++++++++---------------------- src/pulsecore/socket-client.h | 8 ++++--- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/pulsecore/socket-client.c b/src/pulsecore/socket-client.c index 5b5bc5ca..a99193b7 100644 --- a/src/pulsecore/socket-client.c +++ b/src/pulsecore/socket-client.c @@ -77,9 +77,9 @@ struct pa_socket_client { pa_io_event *io_event; pa_time_event *timeout_event; pa_defer_event *defer_event; - void (*callback)(pa_socket_client*c, pa_iochannel *io, void *userdata); + pa_socket_client_cb_t callback; void *userdata; - int local; + pa_bool_t local; #ifdef HAVE_LIBASYNCNS asyncns_t *asyncns; asyncns_query_t * asyncns_query; @@ -87,7 +87,7 @@ struct pa_socket_client { #endif }; -static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) { +static pa_socket_client* socket_client_new(pa_mainloop_api *m) { pa_socket_client *c; pa_assert(m); @@ -96,11 +96,11 @@ static pa_socket_client*pa_socket_client_new(pa_mainloop_api *m) { c->mainloop = m; c->fd = -1; c->io_event = NULL; - c->defer_event = NULL; c->timeout_event = NULL; + c->defer_event = NULL; c->callback = NULL; c->userdata = NULL; - c->local = 0; + c->local = FALSE; #ifdef HAVE_LIBASYNCNS c->asyncns = NULL; @@ -119,15 +119,15 @@ static void free_events(pa_socket_client *c) { c->io_event = NULL; } - if (c->defer_event) { - c->mainloop->defer_free(c->defer_event); - c->defer_event = NULL; - } - if (c->timeout_event) { c->mainloop->time_free(c->timeout_event); c->timeout_event = NULL; } + + if (c->defer_event) { + c->mainloop->defer_free(c->defer_event); + c->defer_event = NULL; + } } static void do_call(pa_socket_client *c) { @@ -177,7 +177,7 @@ finish: pa_socket_client_unref(c); } -static void connect_fixed_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { +static void connect_defer_cb(pa_mainloop_api *m, pa_defer_event *e, void *userdata) { pa_socket_client *c = userdata; pa_assert(m); @@ -223,7 +223,7 @@ static int do_connect(pa_socket_client *c, const struct sockaddr *sa, socklen_t pa_assert_se(c->io_event = c->mainloop->io_new(c->mainloop, c->fd, PA_IO_EVENT_OUTPUT, connect_io_cb, c)); } else - pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_fixed_cb, c)); + pa_assert_se(c->defer_event = c->mainloop->defer_new(c->mainloop, connect_defer_cb, c)); return 0; } @@ -252,8 +252,7 @@ pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *file memset(&sa, 0, sizeof(sa)); sa.sun_family = AF_UNIX; - strncpy(sa.sun_path, filename, sizeof(sa.sun_path)-1); - sa.sun_path[sizeof(sa.sun_path) - 1] = 0; + pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path)); return pa_socket_client_new_sockaddr(m, (struct sockaddr*) &sa, sizeof(sa)); } @@ -273,7 +272,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size switch (sa->sa_family) { case AF_UNIX: - c->local = 1; + c->local = TRUE; break; case AF_INET: @@ -285,7 +284,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size break; default: - c->local = 0; + c->local = FALSE; } if ((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) { @@ -294,6 +293,7 @@ static int sockaddr_prepare(pa_socket_client *c, const struct sockaddr *sa, size } pa_make_fd_cloexec(c->fd); + if (sa->sa_family == AF_INET || sa->sa_family == AF_INET6) pa_make_tcp_socket_low_delay(c->fd); else @@ -312,7 +312,7 @@ pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct pa_assert(sa); pa_assert(salen > 0); - pa_assert_se(c = pa_socket_client_new(m)); + pa_assert_se(c = socket_client_new(m)); if (sockaddr_prepare(c, sa, salen) < 0) goto fail; @@ -361,7 +361,7 @@ pa_socket_client* pa_socket_client_ref(pa_socket_client *c) { return c; } -void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata) { +void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); @@ -489,23 +489,22 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam hints.ai_family = a.type == PA_PARSED_ADDRESS_TCP4 ? PF_INET : (a.type == PA_PARSED_ADDRESS_TCP6 ? PF_INET6 : PF_UNSPEC); hints.ai_socktype = SOCK_STREAM; -#ifdef HAVE_LIBASYNCNS +#if defined(HAVE_LIBASYNCNS) { asyncns_t *asyncns; if (!(asyncns = asyncns_new(1))) goto finish; - c = pa_socket_client_new(m); + pa_assert_se(c = socket_client_new(m)); c->asyncns = asyncns; c->asyncns_io_event = m->io_new(m, asyncns_fd(c->asyncns), PA_IO_EVENT_INPUT, asyncns_cb, c); c->asyncns_query = asyncns_getaddrinfo(c->asyncns, a.path_or_host, port, &hints); pa_assert(c->asyncns_query); start_timeout(c); } -#else /* HAVE_LIBASYNCNS */ +#elif defined(HAVE_GETADDRINFO) { -#ifdef HAVE_GETADDRINFO int ret; struct addrinfo *res = NULL; @@ -520,7 +519,9 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam } freeaddrinfo(res); -#else /* HAVE_GETADDRINFO */ + } +#else + { struct hostent *host = NULL; struct sockaddr_in s; @@ -546,7 +547,6 @@ pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char*nam if ((c = pa_socket_client_new_sockaddr(m, (struct sockaddr*)&s, sizeof(s)))) start_timeout(c); -#endif /* HAVE_GETADDRINFO */ } #endif /* HAVE_LIBASYNCNS */ } @@ -561,7 +561,7 @@ finish: /* Return non-zero when the target sockaddr is considered local. "local" means UNIX socket or TCP socket on localhost. Other local IP addresses are not considered local. */ -int pa_socket_client_is_local(pa_socket_client *c) { +pa_bool_t pa_socket_client_is_local(pa_socket_client *c) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); diff --git a/src/pulsecore/socket-client.h b/src/pulsecore/socket-client.h index b1d58eff..41e8c3bd 100644 --- a/src/pulsecore/socket-client.h +++ b/src/pulsecore/socket-client.h @@ -34,17 +34,19 @@ struct sockaddr; typedef struct pa_socket_client pa_socket_client; +typedef void (*pa_socket_client_cb_t)(pa_socket_client *c, pa_iochannel*io, void *userdata); + pa_socket_client* pa_socket_client_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port); pa_socket_client* pa_socket_client_new_ipv6(pa_mainloop_api *m, uint8_t address[16], uint16_t port); pa_socket_client* pa_socket_client_new_unix(pa_mainloop_api *m, const char *filename); pa_socket_client* pa_socket_client_new_sockaddr(pa_mainloop_api *m, const struct sockaddr *sa, size_t salen); pa_socket_client* pa_socket_client_new_string(pa_mainloop_api *m, const char *a, uint16_t default_port); -void pa_socket_client_unref(pa_socket_client *c); pa_socket_client* pa_socket_client_ref(pa_socket_client *c); +void pa_socket_client_unref(pa_socket_client *c); -void pa_socket_client_set_callback(pa_socket_client *c, void (*on_connection)(pa_socket_client *c, pa_iochannel*io, void *userdata), void *userdata); +void pa_socket_client_set_callback(pa_socket_client *c, pa_socket_client_cb_t on_connection, void *userdata); -int pa_socket_client_is_local(pa_socket_client *c); +pa_bool_t pa_socket_client_is_local(pa_socket_client *c); #endif -- cgit From bb4f83b901c2c18e8bec5b044b1c624759a0113b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 2 May 2008 01:26:10 +0000 Subject: only send PA_SINK_MESSAGE_SET_STATE if there's still an asyncmsqg around to do so git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2347 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 5 +++-- src/pulsecore/source.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index a2a02ebf..6c427e15 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -272,8 +272,9 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { if ((ret = s->set_state(s, state)) < 0) return -1; - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) - return -1; + if (s->asyncmsgq) + if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) + return -1; s->state = state; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 4a2173ca..efd6dab0 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -235,8 +235,9 @@ static int source_set_state(pa_source *s, pa_source_state_t state) { if ((ret = s->set_state(s, state)) < 0) return -1; - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) - return -1; + if (s->asyncmsgq) + if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) < 0) + return -1; s->state = state; -- cgit From 43a30a2ff176302e68813f0021aee95799560d78 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 2 May 2008 13:08:15 +0000 Subject: Fix setrlimit() return value comparsion. git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2350 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/daemon/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daemon/main.c b/src/daemon/main.c index b1ba5a31..b6260bb6 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -493,7 +493,7 @@ int main(int argc, char *argv[]) { else { rl.rlim_max = rl.rlim_cur = 9; - if (setrlimit(RLIMIT_RTPRIO, &rl) < 0) { + if (setrlimit(RLIMIT_RTPRIO, &rl) >= 0) { pa_log_info("Successfully increased RLIMIT_RTPRIO"); drop = TRUE; } else -- cgit From ff09fa391ae067c3ab0f84e6cb3bc1e88a92ec62 Mon Sep 17 00:00:00 2001 From: Tanu Kaskinen Date: Fri, 2 May 2008 13:12:51 +0000 Subject: Fix typo: "now"->"not". git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2351 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/daemon/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/daemon/main.c b/src/daemon/main.c index b6260bb6..f1721b2f 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -469,7 +469,7 @@ int main(int argc, char *argv[]) { #endif if (conf->high_priority && !pa_can_high_priority()) - pa_log_warn("High-priority scheduling enabled in configuration but now allowed by policy."); + pa_log_warn("High-priority scheduling enabled in configuration but not allowed by policy."); if (conf->high_priority && conf->cmd == PA_CMD_DAEMON) pa_raise_priority(conf->nice_level); @@ -511,7 +511,7 @@ int main(int argc, char *argv[]) { } if (conf->realtime_scheduling && !pa_can_realtime()) - pa_log_warn("Real-time scheduling enabled in configuration but now allowed by policy."); + pa_log_warn("Real-time scheduling enabled in configuration but not allowed by policy."); LTDL_SET_PRELOADED_SYMBOLS(); pa_ltdl_init(); -- cgit From 59a7467a642e78876f937570d381b1ad959bf612 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 3 May 2008 01:30:40 +0000 Subject: don't require a module name when resolving a dl symbol git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2352 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/ltdl-helper.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c index 711396d8..b83897a6 100644 --- a/src/pulsecore/ltdl-helper.c +++ b/src/pulsecore/ltdl-helper.c @@ -42,12 +42,14 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s pa_void_func_t f; pa_assert(handle); - pa_assert(module); pa_assert(symbol); - if ((f = ((pa_void_func_t) (long) lt_dlsym(handle, symbol)))) + if ((f = ((pa_void_func_t) (size_t) lt_dlsym(handle, symbol)))) return f; + if (!module) + return NULL; + /* As the .la files might have been cleansed from the system, we should * try with the ltdl prefix as well. */ @@ -57,7 +59,7 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s if (!isalnum(*c)) *c = '_'; - f = (pa_void_func_t) (long) lt_dlsym(handle, sn); + f = (pa_void_func_t) (size_t) lt_dlsym(handle, sn); pa_xfree(sn); return f; -- cgit From d2be471ed241273cd1b41c64946d8f0ac47f88ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 3 May 2008 01:31:39 +0000 Subject: make sure to call sink->update_requested_latency() always when we change latency, same for source git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2353 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink.c | 3 --- src/pulsecore/source.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 6c427e15..d3bacbfd 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -1296,9 +1296,6 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) { pa_sink_assert_ref(s); pa_assert(PA_SINK_IS_LINKED(s->thread_info.state)); - if (!s->thread_info.requested_latency_valid) - return; - s->thread_info.requested_latency_valid = FALSE; if (s->update_requested_latency) diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index efd6dab0..fc4734fd 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -783,9 +783,6 @@ void pa_source_invalidate_requested_latency(pa_source *s) { pa_source_assert_ref(s); pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state)); - if (!s->thread_info.requested_latency_valid) - return; - s->thread_info.requested_latency_valid = FALSE; if (s->update_requested_latency) -- cgit From d2da344fee54f759501952685102f5606d3e2aaa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 3 May 2008 01:32:57 +0000 Subject: send PA_SINK_MESSAGE_REMOVE_INPUT only when an asyncmsgq is available, reset resampler only when we really need to git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2354 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink-input.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index bb377c44..317693df 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -349,7 +349,8 @@ void pa_sink_input_unlink(pa_sink_input *i) { i->state = PA_SINK_INPUT_UNLINKED; if (linked) - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL); + if (i->sink->asyncmsgq) + pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL); reset_callbacks(i); @@ -639,7 +640,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite); /* Calculate how much of the rewinded data should actually be rewritten */ - amount = PA_MIN(max_rewrite, i->thread_info.rewrite_nbytes); + amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite); /* Convert back to to sink domain */ r = i->thread_info.resampler ? pa_resampler_result(i->thread_info.resampler, amount) : amount; @@ -648,17 +649,17 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam /* Ok, now update the write pointer */ pa_memblockq_seek(i->thread_info.render_memblockq, -r, PA_SEEK_RELATIVE); - if (amount) { + if (amount > 0) { pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); /* Tell the implementor */ if (i->process_rewind) i->process_rewind(i, amount); - } - /* And reset the resampler */ - if (i->thread_info.resampler) - pa_resampler_reset(i->thread_info.resampler); + /* And reset the resampler */ + if (i->thread_info.resampler) + pa_resampler_reset(i->thread_info.resampler); + } } i->thread_info.rewrite_nbytes = 0; -- cgit From 3167e0f999f0215e27f769ad76869d0950f0a4e5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 3 May 2008 01:33:33 +0000 Subject: follow _unlink() changes from sink-input git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2355 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/source-output.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 7f5f374e..836e30ed 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -273,12 +273,12 @@ void pa_source_output_unlink(pa_source_output*o) { if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) pa_source_output_unref(o); - if (linked) { - pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); - source_output_set_state(o, PA_SOURCE_OUTPUT_UNLINKED); - pa_source_update_status(o->source); - } else - o->state = PA_SOURCE_OUTPUT_UNLINKED; + update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED); + o->state = PA_SOURCE_OUTPUT_UNLINKED; + + if (linked) + if (o->source->asyncmsgq) + pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); reset_callbacks(o); -- cgit From 82caf5a88643e968a3328c1e1fa8fe63f0572084 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 3 May 2008 01:34:45 +0000 Subject: when rewinding after the end of an underrun, make sure to rewind as much as we can, so that we deal properly with changed latencies of the sink git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2356 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-native.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 5fee4ccf..d6e5602e 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -1102,7 +1102,7 @@ static void send_record_stream_killed(record_stream *r) { static void handle_seek(playback_stream *s, int64_t indexw) { playback_stream_assert_ref(s); -/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->underrun, pa_memblockq_is_readable(s->memblockq)); */ +/* pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */ if (s->sink_input->thread_info.underrun_for > 0) { @@ -1110,15 +1110,13 @@ static void handle_seek(playback_stream *s, int64_t indexw) { if (pa_memblockq_is_readable(s->memblockq)) { - size_t u = pa_memblockq_get_length(s->memblockq); - - if (u >= s->sink_input->thread_info.underrun_for) - u = s->sink_input->thread_info.underrun_for; - /* We just ended an underrun, let's ask the sink - * to rewrite */ + * for a complete rewind rewrite */ - pa_sink_input_request_rewind(s->sink_input, u, TRUE, TRUE); + pa_log_debug("Requesting rewind due to end of underrun."); + pa_sink_input_request_rewind(s->sink_input, + s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for, + TRUE, TRUE); } } else { @@ -1126,11 +1124,13 @@ static void handle_seek(playback_stream *s, int64_t indexw) { indexr = pa_memblockq_get_read_index(s->memblockq); - if (indexw < indexr) + if (indexw < indexr) { /* OK, the sink already asked for this data, so * let's have it usk us again */ + pa_log_debug("Requesting rewind due to rewrite."); pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE, FALSE); + } } request_bytes(s); @@ -1162,10 +1162,10 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int pa_assert(chunk); -/* pa_log("sink input post: %lu", (unsigned long) chunk->length); */ - windex = pa_memblockq_get_write_index(s->memblockq); +/* pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */ + if (pa_memblockq_push_align(s->memblockq, chunk) < 0) { pa_log_warn("Failed to push data into queue"); pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL); @@ -1174,6 +1174,8 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int handle_seek(s, windex); +/* pa_log("sink input post2: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */ + return 0; } @@ -1280,7 +1282,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk if (pa_memblockq_peek(s->memblockq, chunk) < 0) { -/* pa_log("UNDERRUN"); */ +/* pa_log("UNDERRUN: %lu", pa_memblockq_get_length(s->memblockq)); */ if (s->drain_request && pa_sink_input_safe_to_remove(i)) { s->drain_request = FALSE; @@ -1288,7 +1290,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } else if (i->thread_info.playing_for > 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL); -/* pa_log("added %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) s->underrun); */ +/* pa_log("adding %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) i->thread_info.underrun_for); */ request_bytes(s); -- cgit From 59835d955bf38b7b49cb30c538f02899b15aa1b7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 3 May 2008 01:36:05 +0000 Subject: explain why a rewind was requested git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2357 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index efb0fd8a..897b955d 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -848,8 +848,10 @@ static void sink_update_requested_latency_cb(pa_sink *s) { current fill level. Thus, let's do a full rewind once, to clear things up. */ - if (u->hwbuf_unused_frames > before) + if (u->hwbuf_unused_frames > before) { + pa_log_debug("Requesting rewind due to latency change."); pa_sink_request_rewind(s, 0); + } } static int process_rewind(struct userdata *u) { @@ -1311,6 +1313,8 @@ int pa__init(pa_module*m) { } else if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) >= 0) { + /* u->hw_dB_max = 0; u->hw_dB_min = -3000; Use this to make valgrind shut up */ + pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", u->hw_dB_min/100.0, u->hw_dB_max/100.0); /* Let's see if this thing actually is useful for muting */ -- cgit From a1c10b5ecb843dbec6a05340ed7297b595a25e91 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 3 May 2008 01:36:42 +0000 Subject: update LADSPA module for glitch-free moed git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2358 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-ladspa-sink.c | 127 +++++++++++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 26 deletions(-) diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 6e0faac7..f7e7345a 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "module-ladspa-sink-symdef.h" #include "ladspa.h" @@ -60,6 +61,8 @@ PA_MODULE_USAGE( "label= " "control="); +#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) + struct userdata { pa_core *core; pa_module *module; @@ -78,6 +81,8 @@ struct userdata { /* This is a dummy buffer. Every port must be connected, but we don't care about control out ports. We connect them all to this single buffer. */ LADSPA_Data control_out; + + pa_memblockq *memblockq; }; static const char* const valid_modargs[] = { @@ -141,7 +146,10 @@ static void sink_request_rewind(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE); + pa_sink_input_request_rewind( + u->sink_input, + s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), + FALSE, FALSE); } /* Called from I/O thread context */ @@ -169,16 +177,29 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert_se(u = i->userdata); - pa_sink_render(u->sink, nbytes, &tchunk); + if (!u->sink) + return -1; + + while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { + pa_memchunk nchunk; + + pa_sink_render(u->sink, nbytes, &nchunk); + pa_memblockq_push(u->memblockq, &nchunk); + pa_memblock_unref(nchunk.memblock); + } + + pa_assert(tchunk.length > 0); fs = pa_frame_size(&i->sample_spec); - n = tchunk.length / fs; + n = PA_MIN(tchunk.length, u->block_size) / fs; pa_assert(n > 0); - chunk->memblock = pa_memblock_new(i->sink->core->mempool, tchunk.length); chunk->index = 0; - chunk->length = tchunk.length; + chunk->length = n*fs; + chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); + + pa_memblockq_drop(u->memblockq, chunk->length); src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); dst = (float*) pa_memblock_acquire(chunk->memblock); @@ -205,7 +226,35 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_assert_se(u = i->userdata); pa_assert(nbytes > 0); - pa_sink_process_rewind(u->sink, nbytes); + if (!u->sink) + return; + + if (u->sink->thread_info.rewind_nbytes > 0) { + size_t max_rewrite, amount; + + max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq); + amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); + u->sink->thread_info.rewind_nbytes = 0; + + if (amount > 0) { + unsigned c; + + pa_memblockq_seek(u->memblockq, -amount, PA_SEEK_RELATIVE); + pa_sink_process_rewind(u->sink, amount); + + pa_log_debug("Resetting plugin"); + + /* Reset the plugin */ + if (u->descriptor->deactivate) + for (c = 0; c < u->channels; c++) + u->descriptor->deactivate(u->handle[c]); + if (u->descriptor->activate) + for (c = 0; c < u->channels; c++) + u->descriptor->activate(u->handle[c]); + } + } + + pa_memblockq_rewind(u->memblockq, nbytes); } /* Called from I/O thread context */ @@ -215,7 +264,10 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_set_max_rewind(u->sink, nbytes); + pa_memblockq_set_maxrewind(u->memblockq, nbytes); + + if (u->sink) + pa_sink_set_max_rewind(u->sink, nbytes); } /* Called from I/O thread context */ @@ -225,10 +277,11 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_detach_within_thread(u->sink); - - pa_sink_set_asyncmsgq(u->sink, NULL); - pa_sink_set_rtpoll(u->sink, NULL); + if (u->sink) { + pa_sink_detach_within_thread(u->sink); + pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); + } } /* Called from I/O thread context */ @@ -238,10 +291,14 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); - pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + if (u->sink) { + pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); + pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + pa_sink_attach_within_thread(u->sink); - pa_sink_attach_within_thread(u->sink); + u->sink->max_latency = u->master->max_latency; + u->sink->min_latency = u->master->min_latency; + } } /* Called from main context */ @@ -251,17 +308,33 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_input_unlink(u->sink_input); - pa_sink_input_unref(u->sink_input); - u->sink_input = NULL; - pa_sink_unlink(u->sink); pa_sink_unref(u->sink); u->sink = NULL; + pa_sink_input_unlink(u->sink_input); + pa_sink_input_unref(u->sink_input); + u->sink_input = NULL; + pa_module_unload_request(u->module); } +/* Called from IO thread context */ +static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + /* If we are added for the first time, ask for a rewinding so that + * we are heard right-away. */ + if (PA_SINK_INPUT_IS_LINKED(state) && + i->thread_info.state == PA_SINK_INPUT_INIT) { + pa_log_debug("Requesting rewind due to state change."); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + } +} + int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; @@ -321,6 +394,7 @@ int pa__init(pa_module*m) { u->master = master; u->sink = NULL; u->sink_input = NULL; + u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL); if (!(e = getenv("LADSPA_PATH"))) e = LADSPA_PATH; @@ -337,7 +411,7 @@ int pa__init(pa_module*m) { goto fail; } - if (!(descriptor_func = (LADSPA_Descriptor_Function) lt_dlsym(m->dl, "ladspa_descriptor"))) { + if (!(descriptor_func = (LADSPA_Descriptor_Function) pa_load_sym(m->dl, NULL, "ladspa_descriptor"))) { pa_log("LADSPA module lacks ladspa_descriptor() symbol."); goto fail; } @@ -345,7 +419,7 @@ int pa__init(pa_module*m) { for (j = 0;; j++) { if (!(d = descriptor_func(j))) { - pa_log("Failed to find plugin label '%s' in plugin '%s'.", plugin, label); + pa_log("Failed to find plugin label '%s' in plugin '%s'.", label, plugin); goto fail; } @@ -587,18 +661,16 @@ int pa__init(pa_module*m) { pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &map); z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("LADSPA Plugin %s on %s", label, z ? z : master->name)); - pa_xfree(t); + pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin); pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label); pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name); pa_proplist_sets(sink_data.proplist, "device.ladspa.maker", d->Maker); pa_proplist_sets(sink_data.proplist, "device.ladspa.copyright", d->Copyright); - pa_proplist_sets(sink_data.proplist, "device.ladspa.unique_id", t = pa_sprintf_malloc("%lu", (unsigned long) d->UniqueID)); - pa_xfree(t); + pa_proplist_setf(sink_data.proplist, "device.ladspa.unique_id", "%lu", (unsigned long) d->UniqueID); - u->sink = pa_sink_new(m->core, &sink_data, 0); + u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -611,7 +683,6 @@ int pa__init(pa_module*m) { u->sink->update_requested_latency = sink_update_requested_latency; u->sink->request_rewind = sink_request_rewind; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); @@ -638,6 +709,7 @@ int pa__init(pa_module*m) { u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; + u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; pa_sink_put(u->sink); @@ -689,6 +761,9 @@ void pa__done(pa_module*m) { if (u->output != u->input) pa_xfree(u->output); + if (u->memblockq) + pa_memblockq_free(u->memblockq); + pa_xfree(u->input); pa_xfree(u->control); -- cgit From 71d14d40c81195ea4a76fe45be06e705f1e5ad05 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sat, 3 May 2008 02:01:29 +0000 Subject: fix remapping sink for glitch-free git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2359 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-ladspa-sink.c | 48 ++++++++++++----------- src/modules/module-remap-sink.c | 82 ++++++++++++++++++++++++++++++---------- 2 files changed, 88 insertions(+), 42 deletions(-) diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index f7e7345a..be509d73 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -264,10 +264,11 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_memblockq_set_maxrewind(u->memblockq, nbytes); + if (!u->sink) + return; - if (u->sink) - pa_sink_set_max_rewind(u->sink, nbytes); + pa_memblockq_set_maxrewind(u->memblockq, nbytes); + pa_sink_set_max_rewind(u->sink, nbytes); } /* Called from I/O thread context */ @@ -277,11 +278,12 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (u->sink) { - pa_sink_detach_within_thread(u->sink); - pa_sink_set_asyncmsgq(u->sink, NULL); - pa_sink_set_rtpoll(u->sink, NULL); - } + if (!u->sink) + return; + + pa_sink_detach_within_thread(u->sink); + pa_sink_set_asyncmsgq(u->sink, NULL); + pa_sink_set_rtpoll(u->sink, NULL); } /* Called from I/O thread context */ @@ -291,14 +293,15 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (u->sink) { - pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); - pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); - pa_sink_attach_within_thread(u->sink); + if (!u->sink) + return; - u->sink->max_latency = u->master->max_latency; - u->sink->min_latency = u->master->min_latency; - } + pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); + pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); + pa_sink_attach_within_thread(u->sink); + + u->sink->max_latency = u->master->max_latency; + u->sink->min_latency = u->master->min_latency; } /* Called from main context */ @@ -663,6 +666,7 @@ int pa__init(pa_module*m) { z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin); pa_proplist_sets(sink_data.proplist, "device.ladspa.label", d->Label); pa_proplist_sets(sink_data.proplist, "device.ladspa.name", d->Name); @@ -693,7 +697,7 @@ int pa__init(pa_module*m) { sink_input_data.module = m; sink_input_data.sink = u->master; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream"); - pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "routing"); + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); @@ -741,16 +745,16 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; - if (u->sink_input) { - pa_sink_input_unlink(u->sink_input); - pa_sink_input_unref(u->sink_input); - } - if (u->sink) { pa_sink_unlink(u->sink); pa_sink_unref(u->sink); } + if (u->sink_input) { + pa_sink_input_unlink(u->sink_input); + pa_sink_input_unref(u->sink_input); + } + for (c = 0; c < u->channels; c++) if (u->handle[c]) { if (u->descriptor->deactivate) diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index f68b7191..c2adf2cb 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -119,7 +119,10 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, FALSE, FALSE); + pa_sink_input_request_rewind( + u->sink_input, + s->thread_info.rewind_nbytes, + FALSE, FALSE); } /* Called from I/O thread context */ @@ -143,6 +146,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert_se(u = i->userdata); + if (!u->sink) + return -1; + pa_sink_render(u->sink, nbytes, chunk); return 0; } @@ -155,7 +161,18 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_assert_se(u = i->userdata); pa_assert(nbytes > 0); - pa_sink_process_rewind(u->sink, nbytes); + if (!u->sink) + return; + + if (u->sink->thread_info.rewind_nbytes > 0) { + size_t amount; + + amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes); + u->sink->thread_info.rewind_nbytes = 0; + + if (amount > 0) + pa_sink_process_rewind(u->sink, amount); + } } /* Called from I/O thread context */ @@ -165,6 +182,9 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); + if (!u->sink) + return; + pa_sink_set_max_rewind(u->sink, nbytes); } @@ -175,8 +195,10 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_detach_within_thread(u->sink); + if (!u->sink) + return; + pa_sink_detach_within_thread(u->sink); pa_sink_set_asyncmsgq(u->sink, NULL); pa_sink_set_rtpoll(u->sink, NULL); } @@ -188,10 +210,15 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); + if (!u->sink) + return; + pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); pa_sink_set_rtpoll(u->sink, i->sink->rtpoll); - pa_sink_attach_within_thread(u->sink); + + u->sink->max_latency = u->master->max_latency; + u->sink->min_latency = u->master->min_latency; } /* Called from main context */ @@ -201,23 +228,38 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - pa_sink_input_unlink(u->sink_input); - pa_sink_input_unref(u->sink_input); - u->sink_input = NULL; - pa_sink_unlink(u->sink); pa_sink_unref(u->sink); u->sink = NULL; + pa_sink_input_unlink(u->sink_input); + pa_sink_input_unref(u->sink_input); + u->sink_input = NULL; + pa_module_unload_request(u->module); } +/* Called from IO thread context */ +static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + /* If we are added for the first time, ask for a rewinding so that + * we are heard right-away. */ + if (PA_SINK_INPUT_IS_LINKED(state) && + i->thread_info.state == PA_SINK_INPUT_INIT) { + pa_log_debug("Requesting rewind due to state change."); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + } +} + int pa__init(pa_module*m) { struct userdata *u; pa_sample_spec ss; pa_channel_map sink_map, stream_map; pa_modargs *ma; - char *t; const char *k; pa_sink *master; pa_sink_input_new_data sink_input_data; @@ -273,11 +315,11 @@ int pa__init(pa_module*m) { pa_sink_new_data_set_sample_spec(&sink_data, &ss); pa_sink_new_data_set_channel_map(&sink_data, &sink_map); k = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); - pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Remapped %s", k ? k : master->name)); - pa_xfree(t); + pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Remapped %s", k ? k : master->name); pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); - u->sink = pa_sink_new(m->core, &sink_data, 0); + u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY); pa_sink_new_data_done(&sink_data); if (!u->sink) { @@ -290,7 +332,6 @@ int pa__init(pa_module*m) { u->sink->update_requested_latency = sink_update_requested_latency; u->sink->request_rewind = sink_request_rewind; u->sink->userdata = u; - u->sink->flags = PA_SINK_LATENCY; pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); pa_sink_set_rtpoll(u->sink, master->rtpoll); @@ -301,7 +342,7 @@ int pa__init(pa_module*m) { sink_input_data.module = m; sink_input_data.sink = u->master; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream"); - pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "routing"); + pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map); @@ -317,6 +358,7 @@ int pa__init(pa_module*m) { u->sink_input->kill = sink_input_kill_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; + u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; pa_sink_put(u->sink); @@ -343,15 +385,15 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; - if (u->sink_input) { - pa_sink_input_unlink(u->sink_input); - pa_sink_input_unref(u->sink_input); - } - if (u->sink) { pa_sink_unlink(u->sink); pa_sink_unref(u->sink); } + if (u->sink_input) { + pa_sink_input_unlink(u->sink_input); + pa_sink_input_unref(u->sink_input); + } + pa_xfree(u); } -- cgit From 4fa6cb4589516226fd8707610eeb24b1bdaf3c45 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 May 2008 21:10:53 +0000 Subject: add a few more asserts, don't allow pa_limit_caps() to fail git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2370 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/daemon/caps.c | 28 +++++++++------------------- src/daemon/caps.h | 2 +- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/daemon/caps.c b/src/daemon/caps.c index d78e9689..e936d6bb 100644 --- a/src/daemon/caps.c +++ b/src/daemon/caps.c @@ -85,31 +85,21 @@ void pa_drop_root(void) { #if defined(HAVE_SYS_CAPABILITY_H) && defined(HAVE_SYS_PRCTL_H) /* Limit permitted capabilities set to CAPSYS_NICE */ -int pa_limit_caps(void) { - int r = -1; +void pa_limit_caps(void) { cap_t caps; cap_value_t nice_cap = CAP_SYS_NICE; pa_assert_se(caps = cap_init()); + pa_assert_se(cap_clear(caps) == 0); + pa_assert_se(cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET) == 0); + pa_assert_se(cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET) == 0); + pa_assert_se(cap_set_proc(caps) == 0); - cap_clear(caps); - cap_set_flag(caps, CAP_EFFECTIVE, 1, &nice_cap, CAP_SET); - cap_set_flag(caps, CAP_PERMITTED, 1, &nice_cap, CAP_SET); - - if (cap_set_proc(caps) < 0) - goto fail; - - if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) - goto fail; + pa_assert_se(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0); pa_log_info("Dropped capabilities successfully."); - r = 1; - -fail: - cap_free(caps); - - return r; + pa_assert_se(cap_free(caps) == 0); } /* Drop all capabilities, effectively becoming a normal user */ @@ -119,9 +109,9 @@ void pa_drop_caps(void) { pa_assert_se(prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) == 0); pa_assert_se(caps = cap_init()); - cap_clear(caps); + pa_assert_se(cap_clear(caps) == 0); pa_assert_se(cap_set_proc(caps) == 0); - cap_free(caps); + pa_assert_se(cap_free(caps) == 0); } #else diff --git a/src/daemon/caps.h b/src/daemon/caps.h index 91c88418..5b21f12e 100644 --- a/src/daemon/caps.h +++ b/src/daemon/caps.h @@ -26,6 +26,6 @@ void pa_drop_root(void); void pa_drop_caps(void); -int pa_limit_caps(void); +void pa_limit_caps(void); #endif -- cgit From dee3555b2a614e3fa824533125ab4b210d7f8377 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 May 2008 21:11:55 +0000 Subject: rename 'routing' to 'filter' git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2371 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/proplist.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 40c47759..d1bf371f 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -38,7 +38,7 @@ PA_C_DECL_BEGIN * media.filename * media.icon * media.icon_name - * media.role video, music, game, event, phone, production, routing, abstract + * media.role video, music, game, event, phone, production, filter, abstract * event.id button-click, session-login * event.x11.display * event.x11.xid @@ -60,7 +60,7 @@ PA_C_DECL_BEGIN * device.bus_path * device.serial * device.vendor_product_id - * device.class sound, modem, monitor + * device.class sound, modem, monitor, filter * 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 -- cgit From 91fbb691a2d46402f64c9ffc337ec35e6df8e262 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 6 May 2008 21:12:39 +0000 Subject: explain why changing rlimits at this time is safe git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2372 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/daemon/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/daemon/main.c b/src/daemon/main.c index f1721b2f..789d104b 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -465,6 +465,13 @@ int main(int argc, char *argv[]) { } #ifdef HAVE_SYS_RESOURCE_H + /* Reset resource limits. If we are run as root (for system mode) + * this might end up increasing the limits, which is intended + * behaviour. For all other cases, i.e. started as normal user, or + * SUID root at this point we should have no CAP_SYS_RESOURCE and + * increasing the limits thus should fail. Which is, too, intended + * behaviour */ + set_all_rlimits(conf); #endif -- cgit From 44241ac2434ee6c5e66b97f5b89a892aa8f75001 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 01:34:54 +0000 Subject: define callback function types; allow pa_signal_done() to be called even without prior pa_signal_init() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2377 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/mainloop-signal.c | 23 +++++++++++------------ src/pulse/mainloop-signal.h | 16 ++++++++++------ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c index 8ad465bd..91c6bf6d 100644 --- a/src/pulse/mainloop-signal.c +++ b/src/pulse/mainloop-signal.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio is free software; you can redistribute it and/or modify @@ -55,9 +55,9 @@ struct pa_signal_event { #else void (*saved_handler)(int sig); #endif - void (*callback) (pa_mainloop_api*a, pa_signal_event *e, int sig, void *userdata); void *userdata; - void (*destroy_callback) (pa_mainloop_api*a, pa_signal_event*e, void *userdata); + pa_signal_cb_t callback; + pa_signal_destroy_cb_t destroy_callback; pa_signal_event *previous, *next; }; @@ -74,6 +74,7 @@ static void signal_handler(int sig) { #ifndef HAVE_SIGACTION signal(sig, signal_handler); #endif + pa_write(signal_pipe[1], &sig, sizeof(sig), NULL); errno = saved_errno; @@ -142,23 +143,21 @@ int pa_signal_init(pa_mainloop_api *a) { } void pa_signal_done(void) { - pa_assert(api); - pa_assert(signal_pipe[0] >= 0); - pa_assert(signal_pipe[1] >= 0); - pa_assert(io_event); - while (signals) pa_signal_free(signals); - api->io_free(io_event); - io_event = NULL; + if (io_event) { + pa_assert(api); + api->io_free(io_event); + io_event = NULL; + } pa_close_pipe(signal_pipe); api = NULL; } -pa_signal_event* pa_signal_new(int sig, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata) { +pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t _callback, void *userdata) { pa_signal_event *e = NULL; #ifdef HAVE_SIGACTION @@ -223,7 +222,7 @@ void pa_signal_free(pa_signal_event *e) { pa_xfree(e); } -void pa_signal_set_destroy(pa_signal_event *e, void (*_callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)) { +void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t _callback) { pa_assert(e); e->destroy_callback = _callback; diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h index 50aa99ce..bdb0f738 100644 --- a/src/pulse/mainloop-signal.h +++ b/src/pulse/mainloop-signal.h @@ -6,7 +6,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio is free software; you can redistribute it and/or modify @@ -39,23 +39,27 @@ PA_C_DECL_BEGIN * signals. However, you may hook signal support into an abstract main loop via the routines defined herein. */ +/** An opaque UNIX signal event source object */ +typedef struct pa_signal_event pa_signal_event; + +typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata); + +typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata); + /** Initialize the UNIX signal subsystem and bind it to the specified main loop */ int pa_signal_init(pa_mainloop_api *api); /** Cleanup the signal subsystem */ void pa_signal_done(void); -/** An opaque UNIX signal event source object */ -typedef struct pa_signal_event pa_signal_event; - /** Create a new UNIX signal event source object */ -pa_signal_event* pa_signal_new(int sig, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata), void *userdata); +pa_signal_event* pa_signal_new(int sig, pa_signal_cb_t callback, void *userdata); /** Free a UNIX signal event source object */ void pa_signal_free(pa_signal_event *e); /** Set a function that is called when the signal event source is destroyed. Use this to free the userdata argument if required */ -void pa_signal_set_destroy(pa_signal_event *e, void (*callback) (pa_mainloop_api *api, pa_signal_event*e, void *userdata)); +void pa_signal_set_destroy(pa_signal_event *e, pa_signal_destroy_cb_t callback); PA_C_DECL_END -- cgit From 6c28f1d5b962192eaecb78c840377d2a2af05b77 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 01:36:44 +0000 Subject: decrease verbosity a bit git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2378 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 897b955d..4a997cd1 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -258,7 +258,7 @@ static int mmap_write(struct userdata *u) { frames = n = n - u->hwbuf_unused_frames; - pa_log_debug("%lu frames to write", (unsigned long) frames); +/* pa_log_debug("%lu frames to write", (unsigned long) frames);*/ if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { @@ -308,7 +308,7 @@ static int mmap_write(struct userdata *u) { u->frame_index += frames; - pa_log_debug("wrote %lu frames", (unsigned long) frames); +/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */ if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) return work_done; @@ -349,7 +349,7 @@ static int unix_write(struct userdata *u) { n -= u->hwbuf_unused_frames; - pa_log_debug("%lu frames to write", (unsigned long) frames); +/* pa_log_debug("%lu frames to write", (unsigned long) frames); */ if (u->memchunk.length <= 0) pa_sink_render(u->sink, n * u->frame_size, &u->memchunk); @@ -389,7 +389,7 @@ static int unix_write(struct userdata *u) { u->frame_index += frames; - pa_log_debug("wrote %lu frames", (unsigned long) frames); +/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */ if (PA_LIKELY(frames >= n)) return work_done; -- cgit From 9d7fde5fe33aeebf2b0047f63359d4b051c9579c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 01:37:42 +0000 Subject: rework the rewinding logic once again, fixing git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2379 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/sink-input.c | 142 +++++++++++++++++++++++---------------------- src/pulsecore/sink-input.h | 4 +- 2 files changed, 74 insertions(+), 72 deletions(-) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 317693df..0be1cc97 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -250,6 +250,7 @@ pa_sink_input* pa_sink_input_new( i->thread_info.muted = i->muted; i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.rewrite_nbytes = 0; + i->thread_info.rewrite_flush = FALSE; i->thread_info.underrun_for = (uint64_t) -1; i->thread_info.playing_for = 0; @@ -602,8 +603,7 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec * didn't do this for us, we do it here. However, since the sink apparently doesn't support rewinding, we pass 0 here. This still allows rewinding through the render buffer. */ - if (i->thread_info.rewrite_nbytes > 0) - pa_sink_input_process_rewind(i, 0); + pa_sink_input_process_rewind(i, 0); pa_memblockq_drop(i->thread_info.render_memblockq, nbytes); @@ -619,54 +619,59 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam /* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */ - if (i->thread_info.underrun_for > 0) { - /* We don't rewind when we are underrun */ - i->thread_info.rewrite_nbytes = 0; - return; + if (nbytes > 0) { + pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); + pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); } - if (nbytes > 0) - pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); + if (i->thread_info.rewrite_nbytes == (size_t) -1) { - if (i->thread_info.rewrite_nbytes > 0) { - size_t max_rewrite; + /* We were asked to drop all buffered data, and rerequest new + * data from implementor the next time push() is called */ - /* Calculate how much make sense to rewrite at most */ - if ((max_rewrite = nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq)) > 0) { - size_t amount, r; + pa_memblockq_flush(i->thread_info.render_memblockq); - /* Transform into local domain */ - if (i->thread_info.resampler) - max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite); + } else if (i->thread_info.rewrite_nbytes > 0) { + size_t max_rewrite, amount; - /* Calculate how much of the rewinded data should actually be rewritten */ - amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite); + /* Calculate how much make sense to rewrite at most */ + max_rewrite = nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq); - /* Convert back to to sink domain */ - r = i->thread_info.resampler ? pa_resampler_result(i->thread_info.resampler, amount) : amount; + /* Transform into local domain */ + if (i->thread_info.resampler) + max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite); - if (r > 0) - /* Ok, now update the write pointer */ - pa_memblockq_seek(i->thread_info.render_memblockq, -r, PA_SEEK_RELATIVE); + /* Calculate how much of the rewinded data should actually be rewritten */ + amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite); - if (amount > 0) { - pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); + if (amount > 0) { + pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); - /* Tell the implementor */ - if (i->process_rewind) - i->process_rewind(i, amount); + /* Tell the implementor */ + if (i->process_rewind) + i->process_rewind(i, amount); - /* And reset the resampler */ + if (i->thread_info.rewrite_flush) + pa_memblockq_silence(i->thread_info.render_memblockq); + else { + + /* Convert back to to sink domain */ if (i->thread_info.resampler) - pa_resampler_reset(i->thread_info.resampler); + amount = pa_resampler_result(i->thread_info.resampler, amount); + + if (amount > 0) + /* Ok, now update the write pointer */ + pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE); } - } - i->thread_info.rewrite_nbytes = 0; + /* And reset the resampler */ + if (i->thread_info.resampler) + pa_resampler_reset(i->thread_info.resampler); + } } - if (nbytes > 0) - pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); + i->thread_info.rewrite_nbytes = 0; + i->thread_info.rewrite_flush = FALSE; } /* Called from thread context */ @@ -1016,19 +1021,15 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state if (state == PA_SINK_INPUT_CORKED && i->thread_info.state != PA_SINK_INPUT_CORKED) { - /* OK, we're corked, so let's make sure we have total silence - * from now on on this stream */ - pa_memblockq_silence(i->thread_info.render_memblockq); - /* This will tell the implementing sink input driver to rewind * so that the unplayed already mixed data is not lost */ - pa_sink_input_request_rewind(i, 0, FALSE, FALSE); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE); } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) { /* OK, we're being uncorked. Make sure we're not rewound when * the hw buffer is remixed and request a remix. */ - pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE); } if (i->state_change) @@ -1047,12 +1048,12 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t switch (code) { case PA_SINK_INPUT_MESSAGE_SET_VOLUME: i->thread_info.volume = *((pa_cvolume*) userdata); - pa_sink_input_request_rewind(i, 0, FALSE, FALSE); + pa_sink_input_request_rewind(i, 0, TRUE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_SET_MUTE: i->thread_info.muted = PA_PTR_TO_UINT(userdata); - pa_sink_input_request_rewind(i, 0, FALSE, FALSE); + pa_sink_input_request_rewind(i, 0, TRUE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { @@ -1111,59 +1112,60 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { return TRUE; } -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns, pa_bool_t not_here) { +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) { size_t lbq; + /* If 'rewrite' is TRUE the sink is rewound as far as requested + * and possible and the exact value of this is passed back the + * implementor via process_rewind(). If 'flush' is also TRUE all + * already rendered data is also dropped. + * + * If 'rewrite' is FALSE the sink is rewound as far as requested + * and possible and the already rendered data is dropped so that + * in the next iteration we read new data from the + * implementor. This implies 'flush' is TRUE. */ + pa_sink_input_assert_ref(i); + pa_assert(i->thread_info.rewrite_nbytes == 0); /* We don't take rewind requests while we are corked */ if (i->state == PA_SINK_INPUT_CORKED) return; + pa_assert(rewrite || flush); + /* Calculate how much we can rewind locally without having to * touch the sink */ - if (not_here) - lbq = 0; - else + if (rewrite) lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); + else + lbq = 0; /* Check if rewinding for the maximum is requested, and if so, fix up */ if (nbytes <= 0) { - /* Calulate maximum number of bytes that could be rewound in theory */ + /* Calculate maximum number of bytes that could be rewound in theory */ nbytes = i->sink->thread_info.max_rewind + lbq; /* Transform from sink domain */ - nbytes = - i->thread_info.resampler ? - pa_resampler_request(i->thread_info.resampler, nbytes) : - nbytes; + if (i->thread_info.resampler) + nbytes = pa_resampler_request(i->thread_info.resampler, nbytes); } - if (not_here) { - i->thread_info.playing_for = 0; - i->thread_info.underrun_for = (uint64_t) -1; - } else { - /* Increase the number of bytes to rewrite, never decrease */ - if (nbytes < i->thread_info.rewrite_nbytes) - nbytes = i->thread_info.rewrite_nbytes; - + if (rewrite) { /* Make sure to not overwrite over underruns */ - if (!ignore_underruns) - if (nbytes > i->thread_info.playing_for) - nbytes = (size_t) i->thread_info.playing_for; + if (nbytes > i->thread_info.playing_for) + nbytes = (size_t) i->thread_info.playing_for; i->thread_info.rewrite_nbytes = nbytes; - } + } else + i->thread_info.rewrite_nbytes = (size_t) -1; - /* Transform to sink domain */ - nbytes = - i->thread_info.resampler ? - pa_resampler_result(i->thread_info.resampler, nbytes) : - nbytes; + i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0; - if (nbytes <= 0) - return; + /* Transform to sink domain */ + if (i->thread_info.resampler) + nbytes = pa_resampler_result(i->thread_info.resampler, nbytes); if (nbytes > lbq) pa_sink_request_rewind(i->sink, nbytes - lbq); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index b70cb0ac..8edd7ecb 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -156,8 +156,8 @@ struct pa_sink_input { pa_memblockq *render_memblockq; size_t rewrite_nbytes; + pa_bool_t rewrite_flush; uint64_t underrun_for, playing_for; - pa_bool_t ignore_rewind; pa_sink_input *sync_prev, *sync_next; @@ -241,7 +241,7 @@ fully -- or at all. If the request for a rewrite was successful, the sink driver will call ->rewind() and pass the number of bytes that could be rewound in the HW device. This functionality is required for implementing the "zero latency" write-through functionality. */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind, pa_bool_t not_here); +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush); /* Callable by everyone from main thread*/ -- cgit From 8afbdc375c23c21d484df456d22e982a8e26d85b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 01:38:16 +0000 Subject: update to new rewinding logic git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2380 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-ladspa-sink.c | 7 ++----- src/modules/module-remap-sink.c | 7 ++----- src/pulsecore/protocol-native.c | 4 ++-- src/pulsecore/sound-file-stream.c | 7 ++----- 4 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index be509d73..664d73e9 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -146,10 +146,7 @@ static void sink_request_rewind(pa_sink *s) { pa_assert_se(u = s->userdata); /* Just hand this one over to the master sink */ - pa_sink_input_request_rewind( - u->sink_input, - s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), - FALSE, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE); } /* Called from I/O thread context */ @@ -334,7 +331,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) { pa_log_debug("Requesting rewind due to state change."); - pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE); } } diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index c2adf2cb..985afbc8 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -119,10 +119,7 @@ static void sink_request_rewind(pa_sink *s) { pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); - pa_sink_input_request_rewind( - u->sink_input, - s->thread_info.rewind_nbytes, - FALSE, FALSE); + pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE); } /* Called from I/O thread context */ @@ -251,7 +248,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s if (PA_SINK_INPUT_IS_LINKED(state) && i->thread_info.state == PA_SINK_INPUT_INIT) { pa_log_debug("Requesting rewind due to state change."); - pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE); } } diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index d6e5602e..7eeefb84 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -1116,7 +1116,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { pa_log_debug("Requesting rewind due to end of underrun."); pa_sink_input_request_rewind(s->sink_input, s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for, - TRUE, TRUE); + FALSE, TRUE); } } else { @@ -1129,7 +1129,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) { * let's have it usk us again */ pa_log_debug("Requesting rewind due to rewrite."); - pa_sink_input_request_rewind(s->sink_input, indexr - indexw, FALSE, FALSE); + pa_sink_input_request_rewind(s->sink_input, indexr - indexw, TRUE, FALSE); } } diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 918313f8..e209676f 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -245,7 +245,6 @@ int pa_play_file( pa_sample_spec ss; pa_sink_input_new_data data; int fd; - pa_memchunk silence; pa_assert(sink); pa_assert(fname); @@ -347,9 +346,7 @@ int pa_play_file( u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; - pa_sink_input_get_silence(u->sink_input, &silence); - u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&u->sink_input->sample_spec), 1, 1, 0, &silence); - pa_memblock_unref(silence.memblock); + u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL); pa_sink_input_put(u->sink_input); -- cgit From dafcf2053fc652927b4fd55cf904183f89e9ff81 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 02:21:10 +0000 Subject: beefup proplist handling for sound events git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2381 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/core-scache.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index 4036a55b..018b2837 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -141,10 +141,20 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { pa_channel_map_init(&e->channel_map); pa_cvolume_reset(&e->volume, PA_CHANNELS_MAX); + pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event"); + return e; } -int pa_scache_add_item(pa_core *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map, const pa_memchunk *chunk, pa_proplist *p, uint32_t *idx) { +int pa_scache_add_item( + pa_core *c, + const char *name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_memchunk *chunk, + pa_proplist *p, + uint32_t *idx) { + pa_scache_entry *e; char st[PA_SAMPLE_SPEC_SNPRINT_MAX]; pa_channel_map tmap; @@ -198,6 +208,7 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3 pa_channel_map map; pa_memchunk chunk; int r; + pa_proplist *p; #ifdef OS_IS_WIN32 char buf[MAX_PATH]; @@ -213,7 +224,9 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3 if (pa_sound_file_load(c->mempool, filename, &ss, &map, &chunk) < 0) return -1; - r = pa_scache_add_item(c, name, &ss, &map, &chunk, NULL, idx); + p = pa_proplist_new(); + pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename); + r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx); pa_memblock_unref(chunk.memblock); return r; @@ -239,6 +252,8 @@ int pa_scache_add_file_lazy(pa_core *c, const char *name, const char *filename, e->lazy = TRUE; e->filename = pa_xstrdup(filename); + pa_proplist_sets(e->proplist, PA_PROP_MEDIA_FILENAME, filename); + if (!c->scache_auto_unload_event) { struct timeval ntv; pa_gettimeofday(&ntv); @@ -292,7 +307,6 @@ void pa_scache_free(pa_core *c) { int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) { pa_scache_entry *e; - char *t; pa_cvolume r; pa_proplist *merged; @@ -323,12 +337,12 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t merged = pa_proplist_new(); - t = pa_sprintf_malloc("sample:%s", name); - pa_proplist_sets(merged, PA_PROP_MEDIA_NAME, t); - pa_xfree(t); + pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name); pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist); - pa_proplist_update(merged, PA_UPDATE_REPLACE, p); + + if (p) + pa_proplist_update(merged, PA_UPDATE_REPLACE, p); if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) { pa_proplist_free(merged); -- cgit From 9354da4960e4dae0441c840b0de6d3131faf174f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 02:22:30 +0000 Subject: make memchunk/memblockq streams work with glitch-free git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2382 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/play-memblockq.c | 31 +++++++++++++++++++++---------- src/pulsecore/play-memchunk.c | 4 ++-- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index 99b59134..2688f923 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2006 Lennart Poettering + Copyright 2006-2008 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -99,6 +99,21 @@ static void sink_input_kill_cb(pa_sink_input *i) { memblockq_stream_unlink(u); } +/* Called from IO thread context */ +static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { + memblockq_stream *u; + + pa_sink_input_assert_ref(i); + u = MEMBLOCKQ_STREAM(i->userdata); + memblockq_stream_assert_ref(u); + + /* If we are added for the first time, ask for a rewinding so that + * we are heard right-away. */ + if (PA_SINK_INPUT_IS_LINKED(state) && + i->thread_info.state == PA_SINK_INPUT_INIT) + pa_sink_input_request_rewind(i, 0, FALSE, TRUE); +} + static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { memblockq_stream *u; @@ -116,6 +131,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_memblockq_free(u->memblockq); u->memblockq = NULL; + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); } @@ -141,7 +157,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_memblockq_rewind(u->memblockq, nbytes); } -static void sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes) { +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { memblockq_stream *u; pa_sink_input_assert_ref(i); @@ -194,8 +210,9 @@ pa_sink_input* pa_memblockq_sink_input_new( u->sink_input->pop = sink_input_pop_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb; - u->sink_input->update_max_rewind = sink_input_update_max_rewind; + u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->kill = sink_input_kill_cb; + u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; if (q) @@ -255,14 +272,8 @@ void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) { pa_memblockq_free(u->memblockq); if ((u->memblockq = q)) { - pa_memchunk silence; - pa_memblockq_set_prebuf(q, 0); - - pa_sink_input_get_silence(i, &silence); - pa_memblockq_set_silence(q, &silence); - pa_memblock_unref(silence.memblock); - + pa_memblockq_set_silence(q, NULL); pa_memblockq_willneed(q); } } diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c index e4d24a99..67a92138 100644 --- a/src/pulsecore/play-memchunk.c +++ b/src/pulsecore/play-memchunk.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published @@ -54,7 +54,7 @@ int pa_play_memchunk( pa_assert(ss); pa_assert(chunk); - q = pa_memblockq_new(0, chunk->length, 0, pa_frame_size(ss), 0, 0, 0, NULL); + q = pa_memblockq_new(0, chunk->length, 0, pa_frame_size(ss), 1, 1, 0, NULL); pa_assert_se(pa_memblockq_push(q, chunk) >= 0); if ((r = pa_play_memblockq(sink, ss, map, q, volume, p, sink_input_index)) < 0) { -- cgit From d21f45895182ac8a8cdaa08949e3e4db04217880 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 20:34:39 +0000 Subject: fix a memory leak git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2383 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/core-scache.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c index 018b2837..e5546007 100644 --- a/src/pulsecore/core-scache.c +++ b/src/pulsecore/core-scache.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio is free software; you can redistribute it and/or modify @@ -228,6 +228,7 @@ int pa_scache_add_file(pa_core *c, const char *name, const char *filename, uint3 pa_proplist_sets(p, PA_PROP_MEDIA_FILENAME, filename); r = pa_scache_add_item(c, name, &ss, &map, &chunk, p, idx); pa_memblock_unref(chunk.memblock); + pa_proplist_free(p); return r; } -- cgit From 6f4d44bb503409cf4c5a36e1e88517fdd29c7b0f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 20:35:25 +0000 Subject: apparently alsa expects us to free the memory for card names git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2384 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/alsa-util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index 47ed9ace..d212abc2 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -1060,6 +1060,9 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn); else if (n) pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n); + + free(lcn); + free(cn); } int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) { -- cgit From 876d5b408aa23e053b9c35830512038d3227f8e5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 20:36:41 +0000 Subject: fix a race condition when tearing down the ladspa/remap sink git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2385 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-ladspa-sink.c | 16 ++++++++-------- src/modules/module-remap-sink.c | 14 +++++++------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 664d73e9..245efcb0 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -174,7 +174,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert_se(u = i->userdata); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { @@ -223,7 +223,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_assert_se(u = i->userdata); pa_assert(nbytes > 0); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return; if (u->sink->thread_info.rewind_nbytes > 0) { @@ -236,7 +236,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { if (amount > 0) { unsigned c; - pa_memblockq_seek(u->memblockq, -amount, PA_SEEK_RELATIVE); + pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE); pa_sink_process_rewind(u->sink, amount); pa_log_debug("Resetting plugin"); @@ -261,7 +261,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return; pa_memblockq_set_maxrewind(u->memblockq, nbytes); @@ -275,7 +275,7 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return; pa_sink_detach_within_thread(u->sink); @@ -290,7 +290,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return; pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); @@ -309,10 +309,10 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_assert_se(u = i->userdata); pa_sink_unlink(u->sink); + pa_sink_input_unlink(u->sink_input); + pa_sink_unref(u->sink); u->sink = NULL; - - pa_sink_input_unlink(u->sink_input); pa_sink_input_unref(u->sink_input); u->sink_input = NULL; diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 985afbc8..0b9825e1 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -143,7 +143,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); pa_assert_se(u = i->userdata); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return -1; pa_sink_render(u->sink, nbytes, chunk); @@ -158,7 +158,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_assert_se(u = i->userdata); pa_assert(nbytes > 0); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return; if (u->sink->thread_info.rewind_nbytes > 0) { @@ -179,7 +179,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return; pa_sink_set_max_rewind(u->sink, nbytes); @@ -192,7 +192,7 @@ static void sink_input_detach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return; pa_sink_detach_within_thread(u->sink); @@ -207,7 +207,7 @@ static void sink_input_attach_cb(pa_sink_input *i) { pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); - if (!u->sink) + if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state)) return; pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq); @@ -226,10 +226,10 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_assert_se(u = i->userdata); pa_sink_unlink(u->sink); + pa_sink_input_unlink(u->sink_input); + pa_sink_unref(u->sink); u->sink = NULL; - - pa_sink_input_unlink(u->sink_input); pa_sink_input_unref(u->sink_input); u->sink_input = NULL; -- cgit From 21fa1cf857069a507b07d568248aabe98157e43e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 21:09:49 +0000 Subject: double default asyncq size git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2386 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/asyncq.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c index 34506e49..91f0e50b 100644 --- a/src/pulsecore/asyncq.c +++ b/src/pulsecore/asyncq.c @@ -40,7 +40,7 @@ #include "asyncq.h" #include "fdsem.h" -#define ASYNCQ_SIZE 128 +#define ASYNCQ_SIZE 256 /* For debugging purposes we can define _Y to put and extra thread * yield between each operation. */ @@ -73,10 +73,6 @@ PA_STATIC_FLIST_DECLARE(localq, 0, pa_xfree); #define PA_ASYNCQ_CELLS(x) ((pa_atomic_ptr_t*) ((uint8_t*) (x) + PA_ALIGN(sizeof(struct pa_asyncq)))) -static int is_power_of_two(unsigned size) { - return !(size & (size - 1)); -} - static int reduce(pa_asyncq *l, int value) { return value & (unsigned) (l->size - 1); } @@ -87,7 +83,7 @@ pa_asyncq *pa_asyncq_new(unsigned size) { if (!size) size = ASYNCQ_SIZE; - pa_assert(is_power_of_two(size)); + pa_assert(pa_is_power_of_two(size)); l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size)); -- cgit From 7b5c6a34e21a5dffe4da17cee471b10bb1c6010d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 7 May 2008 21:10:19 +0000 Subject: fix recording git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2387 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/stream.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 297e9d7f..3b1975fa 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -1263,7 +1263,8 @@ static void stream_get_timing_info_callback(pa_pdispatch *pd, uint32_t command, goto finish; } - if (o->context->version >= 13) + if (o->context->version >= 13 && + o->stream->direction == PA_STREAM_PLAYBACK) if (pa_tagstruct_getu64(t, &underrun_for) < 0 || pa_tagstruct_getu64(t, &playing_for) < 0) { -- cgit From 1f196e79884618d948927c20ddfcd81ae24ad18c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 8 May 2008 01:08:33 +0000 Subject: fix some comments git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2388 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/asyncq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c index 91f0e50b..8e0dfbc3 100644 --- a/src/pulsecore/asyncq.c +++ b/src/pulsecore/asyncq.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2006 Lennart Poettering + Copyright 2006-2008 Lennart Poettering PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -42,7 +42,7 @@ #define ASYNCQ_SIZE 256 -/* For debugging purposes we can define _Y to put and extra thread +/* For debugging purposes we can define _Y to put an extra thread * yield between each operation. */ /* #define PROFILE */ -- cgit From e97a347325051e3e606b1d4395ba1975f0de838f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 9 May 2008 22:11:57 +0000 Subject: bah, english sucks git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2389 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/def.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pulse/def.h b/src/pulse/def.h index 1a0b9cb6..10722329 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -238,6 +238,10 @@ typedef enum pa_stream_flags { * accordingly. \since 0.9.11 */ } pa_stream_flags_t; + +/** English is an evil language \since 0.9.11 */ +#define PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONOUS + /** Playback and record buffer metrics */ typedef struct pa_buffer_attr { uint32_t maxlength; /**< Maximum length of the -- cgit From 0ea0e0694ecd5e8b626b8e8414d5c8f7107233a3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 9 May 2008 22:27:44 +0000 Subject: make sure the smoother code can deal with incoming data that is out-of-order; start smoothing only when we have at least a configurable number of entries in our history git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2390 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/time-smoother.c | 143 +++++++++++++++++++++++++----------------- src/pulsecore/time-smoother.h | 2 +- 2 files changed, 86 insertions(+), 59 deletions(-) diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c index 97e32b73..9b4be29f 100644 --- a/src/pulsecore/time-smoother.c +++ b/src/pulsecore/time-smoother.c @@ -69,13 +69,14 @@ struct pa_smoother { pa_usec_t ex, ey; /* Point e, which we estimated before and need to smooth to */ double de; /* Gradient we estimated for point e */ + pa_usec_t ry; /* The original y value for ex */ /* History of last measurements */ pa_usec_t history_x[HISTORY_MAX], history_y[HISTORY_MAX]; unsigned history_idx, n_history; /* To even out for monotonicity */ - pa_usec_t last_y; + pa_usec_t last_y, last_x; /* Cached parameters for our interpolation polynomial y=ax^3+b^2+cx */ double a, b, c; @@ -85,13 +86,17 @@ struct pa_smoother { pa_bool_t paused:1; pa_usec_t pause_time; + + unsigned min_history; }; -pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic) { +pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_bool_t monotonic, unsigned min_history) { pa_smoother *s; pa_assert(adjust_time > 0); pa_assert(history_time > 0); + pa_assert(min_history >= 2); + pa_assert(min_history <= HISTORY_MAX); s = pa_xnew(pa_smoother, 1); s->adjust_time = adjust_time; @@ -102,18 +107,20 @@ pa_smoother* pa_smoother_new(pa_usec_t adjust_time, pa_usec_t history_time, pa_b s->px = s->py = 0; s->dp = 1; - s->ex = s->ey = 0; + s->ex = s->ey = s->ry = 0; s->de = 1; s->history_idx = 0; s->n_history = 0; - s->last_y = 0; + s->last_y = s->last_x = 0; s->abc_valid = FALSE; s->paused = FALSE; + s->min_history = min_history; + return s; } @@ -137,9 +144,9 @@ void pa_smoother_free(pa_smoother* s) { static void drop_old(pa_smoother *s, pa_usec_t x) { /* Drop items from history which are too old, but make sure to - * always keep two entries in the history */ + * always keep min_history in the history */ - while (s->n_history > 2) { + while (s->n_history > s->min_history) { if (s->history_x[s->history_idx] + s->history_time >= x) /* This item is still valid, and thus all following ones @@ -184,7 +191,7 @@ static void add_to_history(pa_smoother *s, pa_usec_t x, pa_usec_t y) { s->n_history ++; /* And make sure we don't store more entries than fit in */ - if (s->n_history >= HISTORY_MAX) { + if (s->n_history > HISTORY_MAX) { s->history_idx += s->n_history - HISTORY_MAX; REDUCE(s->history_idx); s->n_history = HISTORY_MAX; @@ -196,7 +203,9 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) { int64_t ax = 0, ay = 0, k, t; double r; - drop_old(s, x); + /* Too few measurements, assume gradient of 1 */ + if (s->n_history < s->min_history) + return 1; /* First, calculate average of all measurements */ i = s->history_idx; @@ -209,10 +218,7 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) { REDUCE_INC(i); } - /* Too few measurements, assume gradient of 1 */ - if (c < 2) - return 1; - + pa_assert(c >= s->min_history); ax /= c; ay /= c; @@ -237,6 +243,39 @@ static double avg_gradient(pa_smoother *s, pa_usec_t x) { return (s->monotonic && r < 0) ? 0 : r; } +static void calc_abc(pa_smoother *s) { + pa_usec_t ex, ey, px, py; + int64_t kx, ky; + double de, dp; + + pa_assert(s); + + if (s->abc_valid) + return; + + /* We have two points: (ex|ey) and (px|py) with two gradients at + * these points de and dp. We do a polynomial + * interpolation of degree 3 with these 6 values */ + + ex = s->ex; ey = s->ey; + px = s->px; py = s->py; + de = s->de; dp = s->dp; + + pa_assert(ex < px); + + /* To increase the dynamic range and symplify calculation, we + * move these values to the origin */ + kx = (int64_t) px - (int64_t) ex; + ky = (int64_t) py - (int64_t) ey; + + /* Calculate a, b, c for y=ax^3+bx^2+cx */ + s->c = de; + s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx; + s->a = (dp/kx - 2*s->b - de/kx) / (3*kx); + + s->abc_valid = TRUE; +} + static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { pa_assert(s); pa_assert(y); @@ -259,36 +298,10 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { } else { - if (!s->abc_valid) { - pa_usec_t ex, ey, px, py; - int64_t kx, ky; - double de, dp; + /* Ok, we're not yet on track, thus let's interpolate, and + * make sure that the first derivative is smooth */ - /* Ok, we're not yet on track, thus let's interpolate, and - * make sure that the first derivative is smooth */ - - /* We have two points: (ex|ey) and (px|py) with two gradients - * at these points de and dp. We do a polynomial interpolation - * of degree 3 with these 6 values */ - - ex = s->ex; ey = s->ey; - px = s->px; py = s->py; - de = s->de; dp = s->dp; - - pa_assert(ex < px); - - /* To increase the dynamic range and symplify calculation, we - * move these values to the origin */ - kx = (int64_t) px - (int64_t) ex; - ky = (int64_t) py - (int64_t) ey; - - /* Calculate a, b, c for y=ax^3+b^2+cx */ - s->c = de; - s->b = (((double) (3*ky)/kx - dp - 2*de)) / kx; - s->a = (dp/kx - 2*s->b - de/kx) / (3*kx); - - s->abc_valid = TRUE; - } + calc_abc(s); /* Move to origin */ x -= s->ex; @@ -307,11 +320,6 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { /* Guarantee monotonicity */ if (s->monotonic) { - if (*y < s->last_y) - *y = s->last_y; - else - s->last_y = *y; - if (deriv && *deriv < 0) *deriv = 0; } @@ -320,6 +328,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) { void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { pa_usec_t ney; double nde; + pa_bool_t is_new; pa_assert(s); @@ -329,12 +338,16 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0; - pa_assert(x >= s->ex); + is_new = x >= s->ex; - /* First, we calculate the position we'd estimate for x, so that - * we can adjust our position smoothly from this one */ - estimate(s, x, &ney, &nde); - s->ex = x; s->ey = ney; s->de = nde; + if (is_new) { + /* First, we calculate the position we'd estimate for x, so that + * we can adjust our position smoothly from this one */ + estimate(s, x, &ney, &nde); + s->ex = x; s->ey = ney; s->de = nde; + + s->ry = y; + } /* Then, we add the new measurement to our history */ add_to_history(s, x, y); @@ -343,8 +356,8 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) { s->dp = avg_gradient(s, x); /* And calculate when we want to be on track again */ - s->px = x + s->adjust_time; - s->py = y + s->dp *s->adjust_time; + s->px = s->ex + s->adjust_time; + s->py = s->ry + s->dp *s->adjust_time; s->abc_valid = FALSE; @@ -361,10 +374,21 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) { x = s->pause_time; x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0; - pa_assert(x >= s->ex); estimate(s, x, &y, NULL); + if (s->monotonic) { + + /* Make sure the querier doesn't jump forth and back. */ + pa_assert(x >= s->last_x); + s->last_x = x; + + if (y < s->last_y) + y = s->last_y; + else + s->last_y = y; + } + /* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */ return y; @@ -416,11 +440,14 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay) x = PA_LIKELY(x >= s->time_offset) ? x - s->time_offset : 0; - pa_assert(x >= s->ex); - estimate(s, x, &ney, &nde); -/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / s->dp), s->dp); */ + /* Play safe and take the larger gradient, so that we wakeup + * earlier when this is used for sleeping */ + if (s->dp > nde) + nde = s->dp; + +/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */ - return (pa_usec_t) ((double) y_delay / s->dp); + return (pa_usec_t) ((double) y_delay / nde); } diff --git a/src/pulsecore/time-smoother.h b/src/pulsecore/time-smoother.h index 85d9f0fd..b301b48c 100644 --- a/src/pulsecore/time-smoother.h +++ b/src/pulsecore/time-smoother.h @@ -29,7 +29,7 @@ typedef struct pa_smoother pa_smoother; -pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic); +pa_smoother* pa_smoother_new(pa_usec_t x_adjust_time, pa_usec_t x_history_time, pa_bool_t monotonic, unsigned min_history); void pa_smoother_free(pa_smoother* s); /* Adds a new value to our dataset. x = local/system time, y = remote time */ -- cgit From 580d56358d9d15792613fc4be886c71059c58a36 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 9 May 2008 22:28:38 +0000 Subject: modify test to generate data events out-of-order git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2391 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/tests/smoother-test.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tests/smoother-test.c b/src/tests/smoother-test.c index 0816e76c..de037724 100644 --- a/src/tests/smoother-test.c +++ b/src/tests/smoother-test.c @@ -47,25 +47,25 @@ int main(int argc, char*argv[]) { srand(0); - for (m = 0, u = 0; u < PA_ELEMENTSOF(msec)-2; u+= 2) { + for (m = 0, u = 0; u < PA_ELEMENTSOF(msec); u+= 2) { - msec[u] = m+1; - msec[u+1] = m + rand() % 2000 - 1000; + msec[u] = m+1 + (rand() % 100) - 50; + msec[u+1] = m + (rand() % 2000) - 1000; m += rand() % 100; + if (msec[u] < 0) + msec[u] = 0; + if (msec[u+1] < 0) msec[u+1] = 0; } - msec[PA_ELEMENTSOF(msec)-2] = 0; - msec[PA_ELEMENTSOF(msec)-1] = 0; - - s = pa_smoother_new(1000*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, TRUE); + s = pa_smoother_new(700*PA_USEC_PER_MSEC, 2000*PA_USEC_PER_MSEC, TRUE, 6); for (x = 0, u = 0; x < PA_USEC_PER_SEC * 10; x += PA_USEC_PER_MSEC) { - while (msec[u] > 0 && (pa_usec_t) msec[u]*PA_USEC_PER_MSEC < x) { + while (u < PA_ELEMENTSOF(msec) && (pa_usec_t) msec[u]*PA_USEC_PER_MSEC < x) { pa_smoother_put(s, msec[u]*PA_USEC_PER_MSEC, msec[u+1]*PA_USEC_PER_MSEC); printf("%i\t\t%i\n", msec[u], msec[u+1]); u += 2; -- cgit From df92b23fa6e520127309c2f63e1f22c7d222e734 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 9 May 2008 22:48:37 +0000 Subject: - Fix moving of sink inputs between sinks - Don't write more than a single buffer size in the ALSA driver at a time, to give the clients time to fill up the memblockq again - Add API for querying the requested latency of a sink input/source output - Drop get_letancy() from vtable of sinks/sources git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2392 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 335 ++++++++++++++++++++---------------- src/modules/module-alsa-source.c | 2 +- src/modules/module-combine.c | 44 ++--- src/modules/module-esound-sink.c | 2 +- src/modules/module-rescue-streams.c | 2 +- src/modules/module-tunnel.c | 38 ++-- src/pulse/stream.c | 34 +++- src/pulsecore/cli-command.c | 2 +- src/pulsecore/cli-text.c | 24 ++- src/pulsecore/protocol-native.c | 67 ++++++-- src/pulsecore/sink-input.c | 190 +++++++------------- src/pulsecore/sink-input.h | 23 ++- src/pulsecore/sink.c | 131 +++++++------- src/pulsecore/sink.h | 8 +- src/pulsecore/source-output.c | 41 ++++- src/pulsecore/source-output.h | 25 +-- src/pulsecore/source.c | 4 - src/pulsecore/source.h | 1 - 18 files changed, 528 insertions(+), 445 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 4a997cd1..97e1e59f 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -131,6 +131,7 @@ struct userdata { pa_smoother *smoother; int64_t frame_index; + uint64_t since_start; snd_pcm_sframes_t hwbuf_unused_frames; }; @@ -162,6 +163,32 @@ static void fix_tsched_watermark(struct userdata *u) { u->tsched_watermark = min_wakeup; } +static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) { + pa_usec_t usec, wm; + + pa_assert(sleep_usec); + pa_assert(process_usec); + + pa_assert(u); + + usec = pa_sink_get_requested_latency_within_thread(u->sink); + + if (usec == (pa_usec_t) -1) + usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec); + +/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ + + wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec); + + if (usec >= wm) { + *sleep_usec = usec - wm; + *process_usec = wm; + } else + *process_usec = *sleep_usec = usec / 2; + +/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */ +} + static int try_recover(struct userdata *u, const char *call, int err) { pa_assert(u); pa_assert(call); @@ -169,16 +196,14 @@ static int try_recover(struct userdata *u, const char *call, int err) { pa_log_debug("%s: %s", call, snd_strerror(err)); - if (err == -EAGAIN) { - pa_log_debug("%s: EAGAIN", call); - return 1; - } + pa_assert(err != -EAGAIN); if (err == -EPIPE) pa_log_debug("%s: Buffer underrun!", call); if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) { u->first = TRUE; + u->since_start = 0; return 0; } @@ -186,20 +211,17 @@ static int try_recover(struct userdata *u, const char *call, int err) { return -1; } -static void check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) { +static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) { size_t left_to_play; - if (u->first || u->after_rewind) - return; - if (n*u->frame_size < u->hwbuf_size) left_to_play = u->hwbuf_size - (n*u->frame_size); else left_to_play = 0; - if (left_to_play > 0) - pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); - else { + if (left_to_play > 0) { +/* pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); */ + } else if (!u->first && !u->after_rewind) { pa_log_info("Underrun!"); if (u->use_tsched) { @@ -213,22 +235,24 @@ static void check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) { (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC); } } + + return left_to_play; } -static int mmap_write(struct userdata *u) { +static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_bool_t checked_left_to_play = FALSE; + pa_usec_t max_sleep_usec, process_usec; + size_t left_to_play; pa_assert(u); pa_sink_assert_ref(u->sink); + if (u->use_tsched) + hw_sleep_time(u, &max_sleep_usec, &process_usec); + for (;;) { - pa_memchunk chunk; - void *p; snd_pcm_sframes_t n; - int err, r; - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t offset, frames; + int r; snd_pcm_hwsync(u->pcm_handle); @@ -239,92 +263,110 @@ static int mmap_write(struct userdata *u) { if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) continue; - else if (r > 0) - return work_done; return r; } - if (!checked_left_to_play) { - check_left_to_play(u, n); - checked_left_to_play = TRUE; - } + left_to_play = check_left_to_play(u, n); + + if (u->use_tsched) + + /* We won't fill up the playback buffer before at least + * half the sleep time is over because otherwise we might + * ask for more data from the clients then they expect. We + * need to guarantee that clients only have to keep around + * a single hw buffer length. */ - /* We only use part of the buffer that matches our - * dynamically requested latency */ + if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > max_sleep_usec/2) + break; if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) - return work_done; + break; - frames = n = n - u->hwbuf_unused_frames; + n -= u->hwbuf_unused_frames; -/* pa_log_debug("%lu frames to write", (unsigned long) frames);*/ +/* pa_log_debug("Filling up"); */ - if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { + for (;;) { + pa_memchunk chunk; + void *p; + int err; + const snd_pcm_channel_area_t *areas; + snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n; - if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) - continue; - else if (r > 0) - return work_done; +/* pa_log_debug("%lu frames to write", (unsigned long) frames); */ - return r; - } + if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { - /* Make sure that if these memblocks need to be copied they will fit into one slot */ - if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size) - frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size; + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) + continue; - /* Check these are multiples of 8 bit */ - pa_assert((areas[0].first & 7) == 0); - pa_assert((areas[0].step & 7)== 0); + return r; + } - /* We assume a single interleaved memory buffer */ - pa_assert((areas[0].first >> 3) == 0); - pa_assert((areas[0].step >> 3) == u->frame_size); + /* Make sure that if these memblocks need to be copied they will fit into one slot */ + if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size) + frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size; - p = (uint8_t*) areas[0].addr + (offset * u->frame_size); + /* Check these are multiples of 8 bit */ + pa_assert((areas[0].first & 7) == 0); + pa_assert((areas[0].step & 7)== 0); - chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE); - chunk.length = pa_memblock_get_length(chunk.memblock); - chunk.index = 0; + /* We assume a single interleaved memory buffer */ + pa_assert((areas[0].first >> 3) == 0); + pa_assert((areas[0].step >> 3) == u->frame_size); - pa_sink_render_into_full(u->sink, &chunk); + p = (uint8_t*) areas[0].addr + (offset * u->frame_size); - /* FIXME: Maybe we can do something to keep this memory block - * a little bit longer around? */ - pa_memblock_unref_fixed(chunk.memblock); + chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE); + chunk.length = pa_memblock_get_length(chunk.memblock); + chunk.index = 0; - if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { + pa_sink_render_into_full(u->sink, &chunk); - if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) - continue; - else if (r > 0) - return work_done; + /* FIXME: Maybe we can do something to keep this memory block + * a little bit longer around? */ + pa_memblock_unref_fixed(chunk.memblock); - return r; - } + if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { - work_done = 1; + if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) + continue; - u->frame_index += frames; + return r; + } -/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */ + work_done = 1; + + u->frame_index += frames; + u->since_start += frames * u->frame_size; + +/* pa_log_debug("wrote %lu frames", (unsigned long) frames); */ - if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) - return work_done; + if (frames >= (snd_pcm_uframes_t) n) + break; + + n -= frames; + } } + + *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec; + return work_done; } -static int unix_write(struct userdata *u) { +static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_bool_t checked_left_to_play = FALSE; + pa_usec_t max_sleep_usec, process_usec; + size_t left_to_play; pa_assert(u); pa_sink_assert_ref(u->sink); + if (u->use_tsched) + hw_sleep_time(u, &max_sleep_usec, &process_usec); + for (;;) { - void *p; - snd_pcm_sframes_t n, frames; + snd_pcm_sframes_t n; int r; snd_pcm_hwsync(u->pcm_handle); @@ -333,67 +375,82 @@ static int unix_write(struct userdata *u) { if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) continue; - else if (r > 0) - return work_done; return r; } - if (!checked_left_to_play) { - check_left_to_play(u, n); - checked_left_to_play = TRUE; - } + left_to_play = check_left_to_play(u, n); + + if (u->use_tsched) + + /* We won't fill up the playback buffer before at least + * half the sleep time is over because otherwise we might + * ask for more data from the clients then they expect. We + * need to guarantee that clients only have to keep around + * a single hw buffer length. */ + + if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > max_sleep_usec/2) + break; if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) - return work_done; + break; n -= u->hwbuf_unused_frames; + for (;;) { + snd_pcm_sframes_t frames; + void *p; + /* pa_log_debug("%lu frames to write", (unsigned long) frames); */ - if (u->memchunk.length <= 0) - pa_sink_render(u->sink, n * u->frame_size, &u->memchunk); + if (u->memchunk.length <= 0) + pa_sink_render(u->sink, n * u->frame_size, &u->memchunk); - pa_assert(u->memchunk.length > 0); + pa_assert(u->memchunk.length > 0); - frames = u->memchunk.length / u->frame_size; + frames = u->memchunk.length / u->frame_size; - if (frames > n) - frames = n; + if (frames > n) + frames = n; - p = pa_memblock_acquire(u->memchunk.memblock); - frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, frames); - pa_memblock_release(u->memchunk.memblock); + p = pa_memblock_acquire(u->memchunk.memblock); + frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, frames); + pa_memblock_release(u->memchunk.memblock); - pa_assert(frames != 0); + pa_assert(frames != 0); - if (PA_UNLIKELY(frames < 0)) { + if (PA_UNLIKELY(frames < 0)) { - if ((r = try_recover(u, "snd_pcm_writei", n)) == 0) - continue; - else if (r > 0) - return work_done; + if ((r = try_recover(u, "snd_pcm_writei", n)) == 0) + continue; - return r; - } + return r; + } - u->memchunk.index += frames * u->frame_size; - u->memchunk.length -= frames * u->frame_size; + u->memchunk.index += frames * u->frame_size; + u->memchunk.length -= frames * u->frame_size; - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - pa_memchunk_reset(&u->memchunk); - } + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + pa_memchunk_reset(&u->memchunk); + } - work_done = 1; + work_done = 1; - u->frame_index += frames; + u->frame_index += frames; + u->since_start += frames * u->frame_size; /* pa_log_debug("wrote %lu frames", (unsigned long) frames); */ - if (PA_LIKELY(frames >= n)) - return work_done; + if (frames >= n) + break; + + n -= frames; + } } + + *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec; + return work_done; } static void update_smoother(struct userdata *u) { @@ -494,35 +551,6 @@ static int suspend(struct userdata *u) { return 0; } -static pa_usec_t hw_sleep_time(struct userdata *u) { - pa_usec_t usec, wm; - - pa_assert(u); - - usec = pa_sink_get_requested_latency_within_thread(u->sink); - - if (usec == (pa_usec_t) -1) - usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec); - - pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); - - wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec); - - if (usec >= wm) - usec -= wm; - else - usec /= 2; - - if (u->first) { - pa_log_debug("Decreasing wakeup time for the first iteration by half."); - usec /= 2; - } - - pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); - - return usec; -} - static int update_sw_params(struct userdata *u) { snd_pcm_uframes_t avail_min; int err; @@ -561,10 +589,10 @@ static int update_sw_params(struct userdata *u) { avail_min = u->hwbuf_unused_frames + 1; if (u->use_tsched) { - pa_usec_t usec; + pa_usec_t sleep_usec, process_usec; - usec = hw_sleep_time(u); - avail_min += pa_usec_to_bytes(usec, &u->sink->sample_spec); + hw_sleep_time(u, &sleep_usec, &process_usec); + avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec); } pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min); @@ -630,6 +658,7 @@ static int unsuspend(struct userdata *u) { /* FIXME: We need to reload the volume somehow */ u->first = TRUE; + u->since_start = 0; pa_log_info("Resumed successfully..."); @@ -932,16 +961,17 @@ static void thread_func(void *userdata) { /* Render some data and write it to the dsp */ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { - int work_done = 0; + int work_done; + pa_usec_t sleep_usec; if (u->sink->thread_info.rewind_nbytes > 0) if (process_rewind(u) < 0) goto fail; if (u->use_mmap) - work_done = mmap_write(u); + work_done = mmap_write(u, &sleep_usec); else - work_done = unix_write(u); + work_done = unix_write(u, &sleep_usec); if (work_done < 0) goto fail; @@ -961,23 +991,34 @@ static void thread_func(void *userdata) { } if (u->use_tsched) { - pa_usec_t usec, cusec; + pa_usec_t cusec; - /* OK, the playback buffer is now full, let's - * calculate when to wake up next */ + if (u->since_start <= u->hwbuf_size) { + + /* USB devices on ALSA seem to hit a buffer + * underrun during the first iterations much + * quicker then we calculate here, probably due to + * the transport latency. To accomodate for that + * we artificially decrease the sleep time until + * we have filled the buffer at least once + * completely.*/ - usec = hw_sleep_time(u); + pa_log_debug("Cutting sleep time for the initial iterations by half."); + sleep_usec /= 2; + } -/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) usec / PA_USEC_PER_MSEC); */ + /* OK, the playback buffer is now full, let's + * calculate when to wake up next */ +/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */ /* Convert from the sound card time domain to the * system time domain */ - cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), usec); + cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec); /* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */ /* We don't trust the conversion, so we wake up whatever comes first */ - pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); + pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec)); } u->first = FALSE; @@ -1014,6 +1055,7 @@ static void thread_func(void *userdata) { goto fail; u->first = TRUE; + u->since_start = 0; } if (revents) @@ -1115,12 +1157,13 @@ int pa__init(pa_module*m) { u->use_mmap = use_mmap; u->use_tsched = use_tsched; u->first = TRUE; + u->since_start = 0; u->after_rewind = FALSE; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->alsa_rtpoll_item = NULL; - u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE); + u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE, 5); usec = pa_rtclock_usec(); pa_smoother_set_time_offset(u->smoother, usec); pa_smoother_pause(u->smoother, usec); diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 9eb6f06b..4838ad27 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -988,7 +988,7 @@ int pa__init(pa_module*m) { pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->alsa_rtpoll_item = NULL; - u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE); + u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5); pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec()); snd_config_update_free_global(); diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c index 2409ef8f..fc8be18d 100644 --- a/src/modules/module-combine.c +++ b/src/modules/module-combine.c @@ -618,35 +618,35 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse } /* Called from main context */ -static pa_usec_t sink_get_latency_cb(pa_sink *s) { - struct userdata *u; +/* static pa_usec_t sink_get_latency_cb(pa_sink *s) { */ +/* struct userdata *u; */ - pa_sink_assert_ref(s); - pa_assert_se(u = s->userdata); +/* pa_sink_assert_ref(s); */ +/* pa_assert_se(u = s->userdata); */ - if (u->master) { - /* If we have a master sink, we just return the latency of it - * and add our own buffering on top */ +/* if (u->master) { */ +/* /\* If we have a master sink, we just return the latency of it */ +/* * and add our own buffering on top *\/ */ - if (!u->master->sink_input) - return 0; +/* if (!u->master->sink_input) */ +/* return 0; */ - return - pa_sink_input_get_latency(u->master->sink_input) + - pa_sink_get_latency(u->master->sink); +/* return */ +/* pa_sink_input_get_latency(u->master->sink_input) + */ +/* pa_sink_get_latency(u->master->sink); */ - } else { - pa_usec_t usec = 0; +/* } else { */ +/* pa_usec_t usec = 0; */ - /* We have no master, hence let's ask our own thread which - * implements the NULL sink */ +/* /\* We have no master, hence let's ask our own thread which */ +/* * implements the NULL sink *\/ */ - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) - return 0; +/* if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) */ +/* return 0; */ - return usec; - } -} +/* return usec; */ +/* } */ +/* } */ static void update_description(struct userdata *u) { int first = 1; @@ -1025,7 +1025,7 @@ int pa__init(pa_module*m) { } u->sink->parent.process_msg = sink_process_msg; - u->sink->get_latency = sink_get_latency_cb; +/* u->sink->get_latency = sink_get_latency_cb; */ u->sink->set_state = sink_set_state; u->sink->userdata = u; diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 2206e2bc..4aa7d674 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -534,7 +534,7 @@ int pa__init(pa_module*m) { u->module = m; m->userdata = u; u->fd = -1; - u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE); + u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10); pa_memchunk_reset(&u->memchunk); u->offset = 0; diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c index dda54735..7241a99f 100644 --- a/src/modules/module-rescue-streams.c +++ b/src/modules/module-rescue-streams.c @@ -75,7 +75,7 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user } while ((i = pa_idxset_first(sink->inputs, NULL))) { - if (pa_sink_input_move_to(i, target, 1) < 0) { + if (pa_sink_input_move_to(i, target) < 0) { pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name); return PA_HOOK_OK; } diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index e3ae5e1f..7a87fd8c 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -577,29 +577,29 @@ static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED } #ifdef TUNNEL_SINK -static pa_usec_t sink_get_latency(pa_sink *s) { - pa_usec_t t, c; - struct userdata *u = s->userdata; +/* static pa_usec_t sink_get_latency(pa_sink *s) { */ +/* pa_usec_t t, c; */ +/* struct userdata *u = s->userdata; */ - pa_sink_assert_ref(s); +/* pa_sink_assert_ref(s); */ - c = pa_bytes_to_usec(u->counter, &s->sample_spec); - t = pa_smoother_get(u->smoother, pa_rtclock_usec()); +/* c = pa_bytes_to_usec(u->counter, &s->sample_spec); */ +/* t = pa_smoother_get(u->smoother, pa_rtclock_usec()); */ - return c > t ? c - t : 0; -} +/* return c > t ? c - t : 0; */ +/* } */ #else -static pa_usec_t source_get_latency(pa_source *s) { - pa_usec_t t, c; - struct userdata *u = s->userdata; +/* static pa_usec_t source_get_latency(pa_source *s) { */ +/* pa_usec_t t, c; */ +/* struct userdata *u = s->userdata; */ - pa_source_assert_ref(s); +/* pa_source_assert_ref(s); */ - c = pa_bytes_to_usec(u->counter, &s->sample_spec); - t = pa_smoother_get(u->smoother, pa_rtclock_usec()); +/* c = pa_bytes_to_usec(u->counter, &s->sample_spec); */ +/* t = pa_smoother_get(u->smoother, pa_rtclock_usec()); */ - return t > c ? t - c : 0; -} +/* return t > c ? t - c : 0; */ +/* } */ #endif static void update_description(struct userdata *u) { @@ -1323,7 +1323,7 @@ int pa__init(pa_module*m) { u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));; u->source = NULL; #endif - u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE); + u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10); u->ctag = 1; u->device_index = u->channel = PA_INVALID_INDEX; u->auth_cookie_in_property = FALSE; @@ -1377,7 +1377,7 @@ int pa__init(pa_module*m) { u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; u->sink->set_state = sink_set_state; - u->sink->get_latency = sink_get_latency; +/* u->sink->get_latency = sink_get_latency; */ u->sink->get_volume = sink_get_volume; u->sink->get_mute = sink_get_mute; u->sink->set_volume = sink_set_volume; @@ -1412,7 +1412,7 @@ int pa__init(pa_module*m) { u->source->parent.process_msg = source_process_msg; u->source->userdata = u; u->source->set_state = source_set_state; - u->source->get_latency = source_get_latency; +/* u->source->get_latency = source_get_latency; */ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 3b1975fa..bd633cf0 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -42,10 +42,11 @@ #include "internal.h" -#define LATENCY_IPOL_INTERVAL_USEC (500*PA_USEC_PER_MSEC) +#define LATENCY_IPOL_INTERVAL_USEC (333*PA_USEC_PER_MSEC) #define SMOOTHER_ADJUST_TIME (1000*PA_USEC_PER_MSEC) #define SMOOTHER_HISTORY_TIME (5000*PA_USEC_PER_MSEC) +#define SMOOTHER_MIN_HISTORY (4) 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); @@ -344,6 +345,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u pa_bool_t suspended; uint32_t di; pa_usec_t usec; + uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0; pa_assert(pd); pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_MOVED || command == PA_COMMAND_RECORD_STREAM_MOVED); @@ -367,14 +369,28 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u } if (c->version >= 13) { - if (pa_tagstruct_get_usec(t, &usec) < 0) { - pa_context_fail(s->context, PA_ERR_PROTOCOL); - goto finish; + + if (s->direction == PA_STREAM_RECORD) { + if (pa_tagstruct_getu32(t, &maxlength) < 0 || + pa_tagstruct_getu32(t, &fragsize) < 0 || + pa_tagstruct_get_usec(t, &usec) < 0) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + } else { + if (pa_tagstruct_getu32(t, &maxlength) < 0 || + pa_tagstruct_getu32(t, &tlength) < 0 || + pa_tagstruct_getu32(t, &prebuf) < 0 || + pa_tagstruct_getu32(t, &minreq) < 0 || + pa_tagstruct_get_usec(t, &usec) < 0) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } } } if (!pa_tagstruct_eof(t)) { - pa_context_fail(s->context, PA_ERR_PROTOCOL); + pa_context_fail(c, PA_ERR_PROTOCOL); goto finish; } @@ -394,6 +410,12 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u s->timing_info.configured_source_usec = usec; else s->timing_info.configured_sink_usec = usec; + + s->buffer_attr.maxlength = maxlength; + s->buffer_attr.fragsize = fragsize; + s->buffer_attr.tlength = tlength; + s->buffer_attr.prebuf = prebuf; + s->buffer_attr.minreq = minreq; } pa_xfree(s->device_name); @@ -861,7 +883,7 @@ static int create_stream( if (s->smoother) pa_smoother_free(s->smoother); - s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS)); + s->smoother = pa_smoother_new(SMOOTHER_ADJUST_TIME, SMOOTHER_HISTORY_TIME, !(flags & PA_STREAM_NOT_MONOTONOUS), SMOOTHER_MIN_HISTORY); x = pa_rtclock_usec(); pa_smoother_set_time_offset(s->smoother, x); diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index b6194f84..925a2e8c 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -1055,7 +1055,7 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf return -1; } - if (pa_sink_input_move_to(si, sink, 0) < 0) { + if (pa_sink_input_move_to(si, sink) < 0) { pa_strbuf_puts(buf, "Moved failed.\n"); return -1; } diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index f251a146..029a7089 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -253,7 +253,13 @@ char *pa_source_output_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u source outputs(s) available.\n", pa_idxset_size(c->source_outputs)); for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; + pa_usec_t cl; + + if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1) + pa_snprintf(clt, sizeof(clt), "n/a"); + else + pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC); pa_assert(o->source); @@ -264,7 +270,8 @@ char *pa_source_output_list_to_string(pa_core *c) { "\tflags: %s%s%s%s%s%s%s%s\n" "\tstate: %s\n" "\tsource: %u <%s>\n" - "\tlatency: %0.2f ms\n" + "\tcurrent latency: %0.2f ms\n" + "\trequested latency: %s\n" "\tsample spec: %s\n" "\tchannel map: %s\n" "\tresample method: %s\n", @@ -281,6 +288,7 @@ char *pa_source_output_list_to_string(pa_core *c) { state_table[pa_source_output_get_state(o)], o->source->index, o->source->name, (double) pa_source_output_get_latency(o) / PA_USEC_PER_MSEC, + clt, pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), pa_resample_method_to_string(pa_source_output_get_resample_method(o))); @@ -315,7 +323,13 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs)); for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; + pa_usec_t cl; + + if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1) + pa_snprintf(clt, sizeof(clt), "n/a"); + else + pa_snprintf(clt, sizeof(clt), "%0.2f ms", (double) cl / PA_USEC_PER_MSEC); pa_assert(i->sink); @@ -328,7 +342,8 @@ char *pa_sink_input_list_to_string(pa_core *c) { "\tsink: %u <%s>\n" "\tvolume: %s\n" "\tmuted: %s\n" - "\tlatency: %0.2f ms\n" + "\tcurrent latency: %0.2f ms\n" + "\trequested latency: %s\n" "\tsample spec: %s\n" "\tchannel map: %s\n" "\tresample method: %s\n", @@ -347,6 +362,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), pa_yes_no(pa_sink_input_get_mute(i)), (double) pa_sink_input_get_latency(i) / PA_USEC_PER_MSEC, + clt, pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 7eeefb84..a824b4da 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -818,6 +818,8 @@ static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlengt *tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); *prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); *minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); + + s->minreq = *minreq; } static playback_stream* playback_stream_new( @@ -933,7 +935,6 @@ static playback_stream* playback_stream_new( *ss = s->sink_input->sample_spec; *map = s->sink_input->channel_map; - s->minreq = *minreq; pa_atomic_store(&s->missing, 0); s->drain_request = FALSE; @@ -1290,14 +1291,14 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk } else if (i->thread_info.playing_for > 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, 0, NULL, NULL); -/* pa_log("adding %llu bytes, total is %llu", (unsigned long long) nbytes, (unsigned long long) i->thread_info.underrun_for); */ +/* pa_log("adding %llu bytes", (unsigned long long) nbytes); */ request_bytes(s); return -1; } -/* pa_log("NOTUNDERRUN"); */ +/* pa_log("NOTUNDERRUN %lu", (unsigned long) chunk->length); */ if (i->thread_info.underrun_for > 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL); @@ -1368,11 +1369,24 @@ static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) { static void sink_input_moved_cb(pa_sink_input *i) { playback_stream *s; pa_tagstruct *t; + uint32_t maxlength, tlength, prebuf, minreq; pa_sink_input_assert_ref(i); s = PLAYBACK_STREAM(i->userdata); playback_stream_assert_ref(s); + maxlength = (uint32_t) pa_memblockq_get_maxlength(s->memblockq); + tlength = (uint32_t) pa_memblockq_get_tlength(s->memblockq); + prebuf = (uint32_t) pa_memblockq_get_prebuf(s->memblockq); + minreq = (uint32_t) pa_memblockq_get_minreq(s->memblockq); + + fix_playback_buffer_attr_pre(s, TRUE, &maxlength, &tlength, &prebuf, &minreq); + pa_memblockq_set_maxlength(s->memblockq, maxlength); + pa_memblockq_set_tlength(s->memblockq, tlength); + pa_memblockq_set_prebuf(s->memblockq, prebuf); + pa_memblockq_set_minreq(s->memblockq, minreq); + fix_playback_buffer_attr_post(s, &maxlength, &tlength, &prebuf, &minreq); + if (s->connection->version < 12) return; @@ -1383,6 +1397,15 @@ static void sink_input_moved_cb(pa_sink_input *i) { pa_tagstruct_putu32(t, i->sink->index); pa_tagstruct_puts(t, i->sink->name); pa_tagstruct_put_boolean(t, pa_sink_get_state(i->sink) == PA_SINK_SUSPENDED); + + if (s->connection->version >= 13) { + pa_tagstruct_putu32(t, maxlength); + pa_tagstruct_putu32(t, tlength); + pa_tagstruct_putu32(t, prebuf); + pa_tagstruct_putu32(t, minreq); + pa_tagstruct_put_usec(t, s->sink_latency); + } + pa_pstream_send_tagstruct(s->connection->pstream, t); } @@ -1447,11 +1470,19 @@ static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) { static void source_output_moved_cb(pa_source_output *o) { record_stream *s; pa_tagstruct *t; + uint32_t maxlength, fragsize; pa_source_output_assert_ref(o); s = RECORD_STREAM(o->userdata); record_stream_assert_ref(s); + fragsize = (uint32_t) s->fragment_size; + maxlength = (uint32_t) pa_memblockq_get_length(s->memblockq); + + fix_record_buffer_attr_pre(s, TRUE, &maxlength, &fragsize); + pa_memblockq_set_maxlength(s->memblockq, maxlength); + fix_record_buffer_attr_post(s, &maxlength, &fragsize); + if (s->connection->version < 12) return; @@ -1462,6 +1493,13 @@ static void source_output_moved_cb(pa_source_output *o) { pa_tagstruct_putu32(t, o->source->index); pa_tagstruct_puts(t, o->source->name); pa_tagstruct_put_boolean(t, pa_source_get_state(o->source) == PA_SOURCE_SUSPENDED); + + if (s->connection->version >= 13) { + pa_tagstruct_putu32(t, maxlength); + pa_tagstruct_putu32(t, fragsize); + pa_tagstruct_put_usec(t, s->source_latency); + } + pa_pstream_send_tagstruct(s->connection->pstream, t); } @@ -1900,27 +1938,28 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t pa_proplist_sets(c->client->proplist, "native-protocol.version", tmp); if (!c->authorized) { - int success = 0; + pa_bool_t success = FALSE; #ifdef HAVE_CREDS const pa_creds *creds; if ((creds = pa_pdispatch_creds(pd))) { if (creds->uid == getuid()) - success = 1; + success = TRUE; else if (c->protocol->auth_group) { int r; gid_t gid; if ((gid = pa_get_gid_of_group(c->protocol->auth_group)) == (gid_t) -1) - pa_log_warn("failed to get GID of group '%s'", c->protocol->auth_group); + pa_log_warn("Failed to get GID of group '%s'", c->protocol->auth_group); else if (gid == creds->gid) - success = 1; + success = TRUE; + if (!success) { if ((r = pa_uid_in_group(creds->uid, c->protocol->auth_group)) < 0) - pa_log_warn("failed to check group membership."); + pa_log_warn("Failed to check group membership."); else if (r > 0) - success = 1; + success = TRUE; } } @@ -1941,7 +1980,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t #endif if (!success && memcmp(c->protocol->auth_cookie, cookie, PA_NATIVE_COOKIE_LENGTH) == 0) - success = 1; + success = TRUE; if (!success) { pa_log_warn("Denied access to client with invalid authorization data."); @@ -3037,6 +3076,9 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u pa_tagstruct_putu32(reply, prebuf); pa_tagstruct_putu32(reply, minreq); + if (c->version >= 13) + pa_tagstruct_put_usec(reply, s->sink_latency); + } else { record_stream *s; pa_bool_t adjust_latency = FALSE; @@ -3063,6 +3105,9 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u reply = reply_new(tag); pa_tagstruct_putu32(reply, maxlength); pa_tagstruct_putu32(reply, fragsize); + + if (c->version >= 13) + pa_tagstruct_put_usec(reply, s->source_latency); } pa_pstream_send_tagstruct(c->pstream, reply); @@ -3600,7 +3645,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY); - if (pa_sink_input_move_to(si, sink, 0) < 0) { + if (pa_sink_input_move_to(si, sink) < 0) { pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID); return; } diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 0be1cc97..d51ff810 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -243,7 +243,6 @@ pa_sink_input* pa_sink_input_new( i->thread_info.state = i->state; i->thread_info.attached = FALSE; pa_atomic_store(&i->thread_info.drained, 1); - pa_atomic_store(&i->thread_info.render_memblockq_is_empty, 0); i->thread_info.sample_spec = i->sample_spec; i->thread_info.resampler = resampler; i->thread_info.volume = i->volume; @@ -410,10 +409,6 @@ void pa_sink_input_put(pa_sink_input *i) { pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index); pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i); - - /* Please note that if you change something here, you have to - change something in pa_sink_input_move() with the ghost stream - registration too. */ } void pa_sink_input_kill(pa_sink_input*i) { @@ -511,10 +506,10 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa * data, so let's just hand out silence */ pa_atomic_store(&i->thread_info.drained, 1); - pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE_ON_READ); + pa_memblockq_seek(i->thread_info.render_memblockq, slength, PA_SEEK_RELATIVE); i->thread_info.playing_for = 0; if (i->thread_info.underrun_for != (uint64_t) -1) - i->thread_info.underrun_for += slength; + i->thread_info.underrun_for += ilength; break; } @@ -551,6 +546,8 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_memchunk rchunk; pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk); +/* pa_log_debug("pushing %lu", (unsigned long) rchunk.length); */ + if (rchunk.memblock) { pa_memblockq_push_align(i->thread_info.render_memblockq, &rchunk); pa_memblock_unref(rchunk.memblock); @@ -571,6 +568,8 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa pa_assert(chunk->length > 0); pa_assert(chunk->memblock); +/* pa_log_debug("peeking %lu", (unsigned long) chunk->length); */ + if (chunk->length > block_size_max_sink) chunk->length = block_size_max_sink; @@ -586,8 +585,6 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa else *volume = i->thread_info.volume; - pa_atomic_store(&i->thread_info.render_memblockq_is_empty, pa_memblockq_is_empty(i->thread_info.render_memblockq)); - return 0; } @@ -599,6 +596,8 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec * pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec)); pa_assert(nbytes > 0); +/* pa_log_debug("dropping %lu", (unsigned long) nbytes); */ + /* If there's still some rewrite request the handle, but the sink didn't do this for us, we do it here. However, since the sink apparently doesn't support rewinding, we pass 0 here. This still @@ -606,12 +605,11 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec * pa_sink_input_process_rewind(i, 0); pa_memblockq_drop(i->thread_info.render_memblockq, nbytes); - - pa_atomic_store(&i->thread_info.render_memblockq_is_empty, pa_memblockq_is_empty(i->thread_info.render_memblockq)); } /* Called from thread context */ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sample spec */) { + size_t lbq; pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); @@ -619,6 +617,8 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam /* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */ + lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); + if (nbytes > 0) { pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); @@ -635,7 +635,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam size_t max_rewrite, amount; /* Calculate how much make sense to rewrite at most */ - max_rewrite = nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq); + max_rewrite = nbytes + lbq; /* Transform into local domain */ if (i->thread_info.resampler) @@ -651,18 +651,16 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam if (i->process_rewind) i->process_rewind(i, amount); - if (i->thread_info.rewrite_flush) - pa_memblockq_silence(i->thread_info.render_memblockq); - else { + /* Convert back to to sink domain */ + if (i->thread_info.resampler) + amount = pa_resampler_result(i->thread_info.resampler, amount); - /* Convert back to to sink domain */ - if (i->thread_info.resampler) - amount = pa_resampler_result(i->thread_info.resampler, amount); + if (amount > 0) + /* Ok, now update the write pointer */ + pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE); - if (amount > 0) - /* Ok, now update the write pointer */ - pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE); - } + if (i->thread_info.rewrite_flush) + pa_memblockq_silence(i->thread_info.render_memblockq); /* And reset the resampler */ if (i->thread_info.resampler) @@ -702,6 +700,7 @@ static pa_usec_t fixup_latency(pa_sink *s, pa_usec_t usec) { } pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) { + pa_sink_input_assert_ref(i); usec = fixup_latency(i->sink, usec); @@ -728,6 +727,21 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) return usec; } +pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) { + pa_usec_t usec = 0; + + pa_sink_input_assert_ref(i); + + if (PA_SINK_INPUT_IS_LINKED(i->state)) + pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL); + else + /* If this sink input is not realized yet, we have to touch + * the thread info data directly */ + usec = i->thread_info.requested_sink_latency; + + return usec; +} + void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) { pa_sink_input_assert_ref(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); @@ -821,11 +835,9 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) { return i->resample_method; } -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately) { +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) { pa_resampler *new_resampler; pa_sink *origin; - pa_usec_t silence_usec = 0; - pa_sink_input_move_info info; pa_sink_input_move_hook_data hook_data; pa_sink_input_assert_ref(i); @@ -881,80 +893,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately hook_data.destination = dest; pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data); - memset(&info, 0, sizeof(info)); - info.sink_input = i; - - if (!immediately) { - pa_usec_t old_latency, new_latency; - - /* Let's do a little bit of Voodoo for compensating latency - * differences. We assume that the accuracy for our - * estimations is still good enough, even though we do these - * operations non-atomic. */ - - old_latency = pa_sink_get_latency(origin); - new_latency = pa_sink_get_latency(dest); - - /* The already resampled data should go to the old sink */ - - if (old_latency >= new_latency) { - - /* The latency of the old sink is larger than the latency - * of the new sink. Therefore to compensate for the - * difference we to play silence on the new one for a - * while */ - - silence_usec = old_latency - new_latency; - - } else { - - /* The latency of new sink is larger than the latency of - * the old sink. Therefore we have to precompute a little - * and make sure that this is still played on the old - * sink, until we can play the first sample on the new - * sink.*/ - - info.buffer_bytes = pa_usec_to_bytes(new_latency - old_latency, &origin->sample_spec); - } - - /* Okey, let's move it */ - - if (info.buffer_bytes > 0) { - pa_proplist *p; - - p = pa_proplist_new(); - pa_proplist_sets(p, PA_PROP_MEDIA_NAME, "Ghost For Moved Stream"); - pa_proplist_sets(p, PA_PROP_MEDIA_ROLE, "routing"); - - info.ghost_sink_input = pa_memblockq_sink_input_new( - origin, - &origin->sample_spec, - &origin->channel_map, - NULL, - NULL, - p); - - pa_proplist_free(p); - - if (info.ghost_sink_input) { - info.ghost_sink_input->thread_info.state = info.ghost_sink_input->state = PA_SINK_INPUT_RUNNING; - info.ghost_sink_input->thread_info.volume = info.ghost_sink_input->volume; - info.ghost_sink_input->thread_info.muted = info.ghost_sink_input->muted; - - info.buffer = pa_memblockq_new(0, MOVE_BUFFER_LENGTH, 0, pa_frame_size(&origin->sample_spec), 0, 0, 0, NULL); - } - } - } - - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, &info, 0, NULL); - - if (info.ghost_sink_input) { - /* Basically, do what pa_sink_input_put() does ...*/ - - pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, info.ghost_sink_input->index); - pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], info.ghost_sink_input); - pa_sink_input_unref(info.ghost_sink_input); - } + pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL); pa_idxset_remove_by_data(origin->inputs, i, NULL); pa_idxset_put(dest->inputs, i, NULL); @@ -965,40 +904,31 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately dest->n_corked++; } - /* Replace resampler */ + /* Replace resampler and render queue */ if (new_resampler != i->thread_info.resampler) { - pa_memchunk silence; if (i->thread_info.resampler) pa_resampler_free(i->thread_info.resampler); i->thread_info.resampler = new_resampler; - /* if the resampler changed, the silence memblock is - * probably invalid now, too */ - - pa_silence_memchunk_get( - &i->sink->core->silence_cache, - i->sink->core->mempool, - &silence, - &dest->sample_spec, - 0); - - pa_memblockq_set_silence(i->thread_info.render_memblockq, &silence); - pa_memblock_unref(silence.memblock); + pa_memblockq_free(i->thread_info.render_memblockq); + i->thread_info.render_memblockq = pa_memblockq_new( + 0, + MEMBLOCKQ_MAXLENGTH, + 0, + pa_frame_size(&i->sink->sample_spec), + 0, + 1, + 0, + &i->sink->silence); } - pa_memblockq_flush(i->thread_info.render_memblockq); - - /* Calculate the new sleeping time */ - if (!immediately) - pa_memblockq_seek(i->thread_info.render_memblockq, pa_usec_to_bytes(silence_usec, &dest->sample_spec), PA_SEEK_RELATIVE); - - pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL); - pa_sink_update_status(origin); pa_sink_update_status(dest); + pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL); + if (i->moved) i->moved(i); @@ -1015,6 +945,9 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state_t state) { pa_sink_input_assert_ref(i); + if (state == i->thread_info.state) + return; + if ((state == PA_SINK_INPUT_DRAINED || state == PA_SINK_INPUT_RUNNING) && !(i->thread_info.state == PA_SINK_INPUT_DRAINED || i->thread_info.state != PA_SINK_INPUT_RUNNING)) pa_atomic_store(&i->thread_info.drained, 1); @@ -1060,7 +993,6 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t pa_usec_t *r = userdata; *r += pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec); - return 0; } @@ -1089,6 +1021,13 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t pa_sink_input_set_requested_latency_within_thread(i, (pa_usec_t) offset); return 0; + + case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: { + pa_usec_t *r = userdata; + + *r = i->thread_info.requested_sink_latency; + return 0; + } } return -1; @@ -1103,11 +1042,12 @@ pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i) { return i->state; } +/* Called from IO context */ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { pa_sink_input_assert_ref(i); - if (i->state == PA_SINK_INPUT_RUNNING || i->state == PA_SINK_INPUT_DRAINED || i->state == PA_SINK_INPUT_CORKED) - return pa_atomic_load(&i->thread_info.render_memblockq_is_empty); + if (PA_SINK_INPUT_IS_LINKED(i->thread_info.state)) + return pa_memblockq_is_empty(i->thread_info.render_memblockq); return TRUE; } @@ -1129,7 +1069,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam pa_assert(i->thread_info.rewrite_nbytes == 0); /* We don't take rewind requests while we are corked */ - if (i->state == PA_SINK_INPUT_CORKED) + if (i->thread_info.state == PA_SINK_INPUT_CORKED) return; pa_assert(rewrite || flush); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index 8edd7ecb..5f146122 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -144,7 +144,7 @@ struct pa_sink_input { struct { pa_sink_input_state_t state; - pa_atomic_t drained, render_memblockq_is_empty; + pa_atomic_t drained; pa_bool_t attached; /* True only between ->attach() and ->detach() calls */ @@ -181,6 +181,7 @@ enum { PA_SINK_INPUT_MESSAGE_SET_RATE, PA_SINK_INPUT_MESSAGE_SET_STATE, PA_SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, + PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY, PA_SINK_INPUT_MESSAGE_MAX }; @@ -243,6 +244,10 @@ could be rewound in the HW device. This functionality is required for implementing the "zero latency" write-through functionality. */ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush); +void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b); + +int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); + /* Callable by everyone from main thread*/ /* External code may request disconnection with this function */ @@ -255,17 +260,14 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i); void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute); int pa_sink_input_get_mute(pa_sink_input *i); -void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b); - -int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate); - pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i); -int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t immediately); +int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest); pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i); -pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i); +pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i); + /* To be used exclusively by the sink driver IO thread */ int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume); @@ -279,12 +281,7 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec); -typedef struct pa_sink_input_move_info { - pa_sink_input *sink_input; - pa_sink_input *ghost_sink_input; - pa_memblockq *buffer; - size_t buffer_bytes; -} pa_sink_input_move_info; +pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i); pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index d3bacbfd..31c3cfc8 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -113,7 +113,6 @@ static void reset_callbacks(pa_sink *s) { s->set_volume = NULL; s->get_mute = NULL; s->set_mute = NULL; - s->get_latency = NULL; s->request_rewind = NULL; s->update_requested_latency = NULL; } @@ -769,9 +768,6 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) { if (!PA_SINK_IS_OPENED(s->state)) return 0; - if (s->get_latency) - return s->get_latency(s); - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) return 0; @@ -930,6 +926,10 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse case PA_SINK_MESSAGE_ADD_INPUT: { pa_sink_input *i = PA_SINK_INPUT(userdata); + /* If you change anything here, make sure to change the + * sink input handling a few lines down at + * PA_SINK_MESSAGE_FINISH_MOVE, too. */ + pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i)); /* Since the caller sleeps in pa_sink_input_put(), we can @@ -965,10 +965,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse * slow start, i.e. need some time to buffer client * samples before beginning streaming. */ - /* If you change anything here, make sure to change the - * ghost sink input handling a few lines down at - * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ - return 0; } @@ -977,7 +973,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse /* If you change anything here, make sure to change the * sink input handling a few lines down at - * PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, too. */ + * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */ pa_sink_input_set_state_within_thread(i, i->state); @@ -1013,85 +1009,88 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse return 0; } - case PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER: { - pa_sink_input_move_info *info = userdata; - int volume_is_norm; + case PA_SINK_MESSAGE_START_MOVE: { + pa_sink_input *i = PA_SINK_INPUT(userdata); /* We don't support moving synchronized streams. */ - pa_assert(!info->sink_input->sync_prev); - pa_assert(!info->sink_input->sync_next); - pa_assert(!info->sink_input->thread_info.sync_next); - pa_assert(!info->sink_input->thread_info.sync_prev); + pa_assert(!i->sync_prev); + pa_assert(!i->sync_next); + pa_assert(!i->thread_info.sync_next); + pa_assert(!i->thread_info.sync_prev); - if (info->sink_input->detach) - info->sink_input->detach(info->sink_input); + if (i->thread_info.state != PA_SINK_INPUT_CORKED) { + pa_usec_t usec = 0; + size_t sink_nbytes, total_nbytes; - pa_assert(info->sink_input->thread_info.attached); - info->sink_input->thread_info.attached = FALSE; - pa_sink_invalidate_requested_latency(info->sink_input->sink); + /* Get the latency of the sink */ + if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) + usec = 0; - if (info->ghost_sink_input) { - pa_assert(info->buffer_bytes > 0); - pa_assert(info->buffer); + sink_nbytes = pa_usec_to_bytes(usec, &s->sample_spec); + total_nbytes = sink_nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq); - volume_is_norm = pa_cvolume_is_norm(&info->sink_input->thread_info.volume); + if (total_nbytes > 0) { + i->thread_info.rewrite_nbytes = i->thread_info.resampler ? pa_resampler_request(i->thread_info.resampler, total_nbytes) : total_nbytes; + i->thread_info.rewrite_flush = TRUE; + pa_sink_input_process_rewind(i, sink_nbytes); + } + } - pa_log_debug("Buffering %lu bytes ...", (unsigned long) info->buffer_bytes); + if (i->detach) + i->detach(i); - while (info->buffer_bytes > 0) { - pa_memchunk memchunk; - pa_cvolume volume; - size_t n; + pa_assert(i->thread_info.attached); + i->thread_info.attached = FALSE; - if (pa_sink_input_peek(info->sink_input, info->buffer_bytes, &memchunk, &volume) < 0) - break; + /* Let's remove the sink input ...*/ + if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index))) + pa_sink_input_unref(i); - n = memchunk.length > info->buffer_bytes ? info->buffer_bytes : memchunk.length; - pa_sink_input_drop(info->sink_input, n); - memchunk.length = n; + pa_sink_invalidate_requested_latency(s); - if (!volume_is_norm) { - pa_memchunk_make_writable(&memchunk, 0); - pa_volume_memchunk(&memchunk, &s->sample_spec, &volume); - } + pa_log_debug("Requesting rewind due to started move"); + pa_sink_request_rewind(s, 0); - if (pa_memblockq_push(info->buffer, &memchunk) < 0) { - pa_memblock_unref(memchunk.memblock); - break; - } + return 0; + } - pa_memblock_unref(memchunk.memblock); - info->buffer_bytes -= n; - } + case PA_SINK_MESSAGE_FINISH_MOVE: { + pa_sink_input *i = PA_SINK_INPUT(userdata); - /* Add the remaining already resampled chunks to the buffer */ - pa_memblockq_splice(info->buffer, info->sink_input->thread_info.render_memblockq); + /* We don't support moving synchronized streams. */ + pa_assert(!i->sync_prev); + pa_assert(!i->sync_next); + pa_assert(!i->thread_info.sync_next); + pa_assert(!i->thread_info.sync_prev); - pa_memblockq_sink_input_set_queue(info->ghost_sink_input, info->buffer); + pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(i->index), pa_sink_input_ref(i)); - pa_log_debug("Buffered %lu bytes ...", (unsigned long) pa_memblockq_get_length(info->buffer)); - } + pa_assert(!i->thread_info.attached); + i->thread_info.attached = TRUE; - /* Let's remove the sink input ...*/ - if (pa_hashmap_remove(s->thread_info.inputs, PA_UINT32_TO_PTR(info->sink_input->index))) - pa_sink_input_unref(info->sink_input); + if (i->attach) + i->attach(i); - /* .. and add the ghost sink input instead */ - if (info->ghost_sink_input) { - pa_hashmap_put(s->thread_info.inputs, PA_UINT32_TO_PTR(info->ghost_sink_input->index), pa_sink_input_ref(info->ghost_sink_input)); - info->ghost_sink_input->thread_info.sync_prev = info->ghost_sink_input->thread_info.sync_next = NULL; + pa_sink_input_update_max_rewind(i, s->thread_info.max_rewind); - pa_sink_input_update_max_rewind(info->ghost_sink_input, s->thread_info.max_rewind); + pa_sink_input_set_requested_latency_within_thread(i, i->thread_info.requested_sink_latency); - pa_assert(!info->ghost_sink_input->thread_info.attached); - info->ghost_sink_input->thread_info.attached = TRUE; + if (i->thread_info.state != PA_SINK_INPUT_CORKED) { + pa_usec_t usec = 0; + size_t nbytes; - if (info->ghost_sink_input->attach) - info->ghost_sink_input->attach(info->ghost_sink_input); - } + /* Get the latency of the sink */ + if (PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) + usec = 0; - pa_sink_invalidate_requested_latency(s); - pa_sink_request_rewind(s, 0); + nbytes = pa_usec_to_bytes(usec, &s->sample_spec); + + if (nbytes > 0) + pa_sink_input_drop(i, nbytes); + + pa_log_debug("Requesting rewind due to finished move"); + pa_sink_request_rewind(s, nbytes); + } return 0; } diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index f25f48cf..f297c8f1 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -118,11 +118,6 @@ struct pa_sink { * message will be sent to the IO thread instead. */ int (*set_mute)(pa_sink *s); /* dito */ - /* Called when the latency is queried. Called from main loop - context. If this is NULL a PA_SINK_MESSAGE_GET_LATENCY message - will be sent to the IO thread instead. */ - pa_usec_t (*get_latency)(pa_sink *s); /* dito */ - /* Called when a rewind request is issued. Called from IO thread * context. */ void (*request_rewind)(pa_sink *s); /* dito */ @@ -166,7 +161,8 @@ typedef enum pa_sink_message { PA_SINK_MESSAGE_GET_LATENCY, PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, PA_SINK_MESSAGE_SET_STATE, - PA_SINK_MESSAGE_REMOVE_INPUT_AND_BUFFER, + PA_SINK_MESSAGE_START_MOVE, + PA_SINK_MESSAGE_FINISH_MOVE, PA_SINK_MESSAGE_ATTACH, PA_SINK_MESSAGE_DETACH, PA_SINK_MESSAGE_MAX diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 836e30ed..3940d768 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -294,6 +294,7 @@ void pa_source_output_unlink(pa_source_output*o) { static void source_output_free(pa_object* mo) { pa_source_output *o = PA_SOURCE_OUTPUT(mo); + pa_assert(o); pa_assert(pa_source_output_refcnt(o) == 0); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) @@ -326,7 +327,7 @@ void pa_source_output_put(pa_source_output *o) { state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; update_n_corked(o, state); - o->thread_info.state = o->state = state; + o->state = state; pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); @@ -470,6 +471,7 @@ static pa_usec_t fixup_latency(pa_source *s, pa_usec_t usec) { } pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) { + pa_source_output_assert_ref(o); usec = fixup_latency(o->source, usec); @@ -496,6 +498,21 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t return usec; } +pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { + pa_usec_t usec = 0; + + pa_source_output_assert_ref(o); + + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) + pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL); + else + /* If this sink input is not realized yet, we have to touch + * the thread info data directly */ + usec = o->thread_info.requested_source_latency; + + return usec; +} + void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { pa_source_output_assert_ref(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); @@ -523,11 +540,11 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) { const char *old; pa_source_output_assert_ref(o); - old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME); - - if (!old && !name) + if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME)) return; + old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME); + if (old && name && !strcmp(old, name)) return; @@ -550,7 +567,7 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_source *origin; - pa_resampler *new_resampler = NULL; + pa_resampler *new_resampler; pa_source_output_move_hook_data hook_data; pa_source_output_assert_ref(o); @@ -594,7 +611,8 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { pa_log_warn("Unsupported resampling operation."); return -1; } - } + } else + new_resampler = NULL; hook_data.source_output = o; hook_data.destination = dest; @@ -640,6 +658,9 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) { pa_source_output_assert_ref(o); + if (state == o->thread_info.state) + return; + if (o->state_change) o->state_change(o, state); @@ -659,7 +680,6 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int pa_usec_t *r = userdata; *r += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec); - return 0; } @@ -678,6 +698,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int pa_source_output_set_requested_latency_within_thread(o, (pa_usec_t) offset); return 0; + + case PA_SINK_INPUT_MESSAGE_GET_REQUESTED_LATENCY: { + pa_usec_t *r = userdata; + + *r = o->thread_info.requested_source_latency; + return 0; + } } return -1; diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 67cb3761..2dadb5c4 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -148,6 +148,7 @@ enum { PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, + PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, PA_SOURCE_OUTPUT_MESSAGE_MAX }; @@ -168,16 +169,16 @@ typedef struct pa_source_output_new_data { pa_resample_method_t resample_method; } pa_source_output_new_data; -typedef struct pa_source_output_move_hook_data { - pa_source_output *source_output; - pa_source *destination; -} pa_source_output_move_hook_data; - pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data); void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec); void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map); void pa_source_output_new_data_done(pa_source_output_new_data *data); +typedef struct pa_source_output_move_hook_data { + pa_source_output *source_output; + pa_source *destination; +} pa_source_output_move_hook_data; + /* To be called by the implementing module only */ pa_source_output* pa_source_output_new( @@ -192,6 +193,10 @@ void pa_source_output_set_name(pa_source_output *i, const char *name); pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec); +void pa_source_output_cork(pa_source_output *i, pa_bool_t b); + +int pa_source_output_set_rate(pa_source_output *o, uint32_t rate); + /* Callable by everyone */ /* External code may request disconnection with this funcion */ @@ -199,26 +204,24 @@ void pa_source_output_kill(pa_source_output*o); pa_usec_t pa_source_output_get_latency(pa_source_output *i); -void pa_source_output_cork(pa_source_output *i, pa_bool_t b); - -int pa_source_output_set_rate(pa_source_output *o, uint32_t rate); - pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o); int pa_source_output_move_to(pa_source_output *o, pa_source *dest); #define pa_source_output_get_state(o) ((o)->state) +pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o); + /* To be used exclusively by the source driver thread */ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk); void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes); void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes); -int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk); - void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state); +int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int64_t offset, pa_memchunk *chunk); + pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec); #endif diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index fc4734fd..426906eb 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -107,7 +107,6 @@ static void reset_callbacks(pa_source *s) { s->set_volume = NULL; s->get_mute = NULL; s->set_mute = NULL; - s->get_latency = NULL; s->update_requested_latency = NULL; } @@ -440,9 +439,6 @@ pa_usec_t pa_source_get_latency(pa_source *s) { if (!PA_SOURCE_IS_OPENED(s->state)) return 0; - if (s->get_latency) - return s->get_latency(s); - if (pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0) return 0; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index cce54620..f9c9cbf9 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -100,7 +100,6 @@ struct pa_source { int (*get_volume)(pa_source *s); /* dito */ int (*set_mute)(pa_source *s); /* dito */ int (*get_mute)(pa_source *s); /* dito */ - pa_usec_t (*get_latency)(pa_source *s); /* dito */ void (*update_requested_latency)(pa_source *s); /* dito */ /* Contains copies of the above data so that the real-time worker -- cgit From f124445f89b19fa6554e9b3210c18f8740059439 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 9 May 2008 23:15:06 +0000 Subject: fix module-sine for glitch-free git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2393 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-sine.c | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c index f6718fd8..3d917054 100644 --- a/src/modules/module-sine.c +++ b/src/modules/module-sine.c @@ -67,12 +67,30 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk pa_assert(chunk); chunk->memblock = pa_memblock_ref(u->memblock); - chunk->length = pa_memblock_get_length(chunk->memblock); - chunk->index = 0; + chunk->length = pa_memblock_get_length(u->memblock) - u->peek_index; + chunk->index = u->peek_index; + + u->peek_index = 0; return 0; } +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + size_t l; + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + l = pa_memblock_get_length(u->memblock); + nbytes %= l; + + if (u->peek_index >= nbytes) + u->peek_index -= nbytes; + else + u->peek_index = l + u->peek_index - nbytes; +} + static void sink_input_kill_cb(pa_sink_input *i) { struct userdata *u; @@ -86,6 +104,20 @@ static void sink_input_kill_cb(pa_sink_input *i) { pa_module_unload_request(u->module); } +/* Called from IO thread context */ +static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { + struct userdata *u; + + pa_sink_input_assert_ref(i); + pa_assert_se(u = i->userdata); + + /* If we are added for the first time, ask for a rewinding so that + * we are heard right-away. */ + if (PA_SINK_INPUT_IS_LINKED(state) && + i->thread_info.state == PA_SINK_INPUT_INIT) + pa_sink_input_request_rewind(i, 0, FALSE, TRUE); +} + static void calc_sine(float *f, size_t l, float freq) { size_t i; @@ -101,7 +133,6 @@ int pa__init(pa_module*m) { pa_sink *sink; pa_sample_spec ss; uint32_t frequency; - char t[256]; void *p; pa_sink_input_new_data data; @@ -137,13 +168,12 @@ int pa__init(pa_module*m) { calc_sine(p, pa_memblock_get_length(u->memblock), frequency); pa_memblock_release(u->memblock); - pa_snprintf(t, sizeof(t), "%u Hz Sine", frequency); - pa_sink_input_new_data_init(&data); data.sink = sink; data.driver = __FILE__; - pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, t); + pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); + pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency); pa_sink_input_new_data_set_sample_spec(&data, &ss); data.module = m; @@ -154,7 +184,9 @@ int pa__init(pa_module*m) { goto fail; u->sink_input->pop = sink_input_pop_cb; + u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->kill = sink_input_kill_cb; + u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->userdata = u; pa_sink_input_put(u->sink_input); -- cgit From aae8beb8cc83244900fe3ab19a12b972833f30ca Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 00:40:36 +0000 Subject: if zero is passed to pa_memblock_new() allocate largest memory block possible from mempool git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2412 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/memblock.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index 33b38745..a5552786 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -204,7 +204,9 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) { pa_memblock *b; pa_assert(p); - pa_assert(length > 0); + + if (length <= 0) + length = pa_mempool_block_size_max(p); if (!(b = pa_memblock_new_pool(p, length))) b = memblock_new_appended(p, length); -- cgit From c5faeb1390e849a61a223ca3c6e2a87c87f3a349 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 00:41:58 +0000 Subject: store peer name in native-protocol.peer property git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2413 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-native.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index a824b4da..ee7e1ed8 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -1038,10 +1038,8 @@ static void request_bytes(playback_stream *s) { previous_missing = pa_atomic_add(&s->missing, m); if (pa_memblockq_prebuf_active(s->memblockq) || - (previous_missing < s->minreq && previous_missing+m >= s->minreq)) { - pa_assert(pa_thread_mq_get()); + (previous_missing < s->minreq && previous_missing+m >= s->minreq)) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); - } } static void send_memblock(connection *c) { @@ -3921,6 +3919,7 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname); c->client = pa_client_new(p->core, __FILE__, cname); + pa_proplist_sets(c->client, "native-protocol.peer", pname); c->client->kill = client_kill_cb; c->client->userdata = c; c->client->module = p->module; -- cgit From 8df6529ee6d1fb53f5fffece06fa820b393daebd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 00:42:46 +0000 Subject: some fixes to make the simple protocol work on glitch-free again git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2414 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-simple.c | 126 +++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 39 deletions(-) diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c index 8ec38fe4..cbe48440 100644 --- a/src/pulsecore/protocol-simple.c +++ b/src/pulsecore/protocol-simple.c @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include "protocol-simple.h" @@ -57,12 +59,13 @@ typedef struct connection { pa_client *client; pa_memblockq *input_memblockq, *output_memblockq; - int dead; + pa_bool_t dead; struct { pa_memblock *current_memblock; - size_t memblock_index, fragment_size; + size_t memblock_index; pa_atomic_t missing; + pa_bool_t underrun; } playback; } connection; @@ -101,7 +104,8 @@ enum { #define PLAYBACK_BUFFER_SECONDS (.5) #define PLAYBACK_BUFFER_FRAGMENTS (10) #define RECORD_BUFFER_SECONDS (5) -#define RECORD_BUFFER_FRAGMENTS (100) +#define DEFAULT_SINK_LATENCY (300*PA_USEC_PER_MSEC) +#define DEFAULT_SOURCE_LATENCY (300*PA_USEC_PER_MSEC) static void connection_unlink(connection *c) { pa_assert(c); @@ -140,8 +144,6 @@ static void connection_free(pa_object *o) { connection *c = CONNECTION(o); pa_assert(c); - connection_unref(c); - if (c->playback.current_memblock) pa_memblock_unref(c->playback.current_memblock); @@ -158,27 +160,33 @@ static int do_read(connection *c) { ssize_t r; size_t l; void *p; + size_t space; connection_assert_ref(c); if (!c->sink_input || (l = pa_atomic_load(&c->playback.missing)) <= 0) return 0; - if (l > c->playback.fragment_size) - l = c->playback.fragment_size; + if (c->playback.current_memblock) { - if (c->playback.current_memblock) - if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) { + space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index; + + if (space <= 0) { pa_memblock_unref(c->playback.current_memblock); c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; } + } if (!c->playback.current_memblock) { - pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, l)); + pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0)); c->playback.memblock_index = 0; + + space = pa_memblock_get_length(c->playback.current_memblock); } + if (l > space) + l = space; + p = pa_memblock_acquire(c->playback.current_memblock); r = pa_iochannel_read(c->io, (uint8_t*) p + c->playback.memblock_index, l); pa_memblock_release(c->playback.current_memblock); @@ -248,16 +256,16 @@ static void do_work(connection *c) { if (c->dead) return; - if (pa_iochannel_is_readable(c->io)) { + if (pa_iochannel_is_readable(c->io)) if (do_read(c) < 0) goto fail; - } else if (pa_iochannel_is_hungup(c->io)) + + if (!c->sink_input && pa_iochannel_is_hungup(c->io)) goto fail; - if (pa_iochannel_is_writable(c->io)) { + if (pa_iochannel_is_writable(c->io)) if (do_write(c) < 0) goto fail; - } return; @@ -266,7 +274,7 @@ fail: if (c->sink_input) { /* If there is a sink input, we first drain what we already have read before shutting down the connection */ - c->dead = 1; + c->dead = TRUE; pa_iochannel_free(c->io); c->io = NULL; @@ -318,15 +326,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int /* New data from the main loop */ pa_memblockq_push_align(c->input_memblockq, chunk); + if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) { + pa_log_debug("Requesting rewind due to end of underrun."); + pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE); + } + /* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */ return 0; } - case SINK_INPUT_MESSAGE_DISABLE_PREBUF: { + case SINK_INPUT_MESSAGE_DISABLE_PREBUF: pa_memblockq_prebuf_disable(c->input_memblockq); return 0; - } case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = userdata; @@ -345,32 +357,60 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int /* Called from thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { connection *c; - int r; - pa_assert(i); + pa_sink_input_assert_ref(i); c = CONNECTION(i->userdata); connection_assert_ref(c); pa_assert(chunk); - if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0) { + if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) { + + c->playback.underrun = TRUE; if (c->dead && pa_sink_input_safe_to_remove(i)) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL); + return -1; } else { - size_t old, new; + size_t m; + + c->playback.underrun = FALSE; - old = pa_memblockq_missing(c->input_memblockq); pa_memblockq_drop(c->input_memblockq, chunk->length); - new = pa_memblockq_missing(c->input_memblockq); + m = pa_memblockq_pop_missing(c->input_memblockq); - if (new > old) { - if (pa_atomic_add(&c->playback.missing, new - old) <= 0) + if (m > 0) + if (pa_atomic_add(&c->playback.missing, m) <= 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); - } + + return 0; } +} + +/* Called from thread context */ +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + connection *c; - return r; + pa_sink_input_assert_ref(i); + c = CONNECTION(i->userdata); + connection_assert_ref(c); + + /* If we are in an underrun, then we don't rewind */ + if (i->thread_info.underrun_for > 0) + return; + + pa_memblockq_rewind(c->input_memblockq, nbytes); +} + +/* Called from thread context */ +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + connection *c; + + pa_sink_input_assert_ref(i); + c = CONNECTION(i->userdata); + connection_assert_ref(c); + + pa_memblockq_set_maxrewind(c->input_memblockq, nbytes); } /* Called from main context */ @@ -386,7 +426,7 @@ static void sink_input_kill_cb(pa_sink_input *i) { static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { connection *c; - pa_assert(o); + pa_source_output_assert_ref(o); c = CONNECTION(o->userdata); pa_assert(c); pa_assert(chunk); @@ -405,7 +445,7 @@ static void source_output_kill_cb(pa_source_output *o) { static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { connection*c; - pa_assert(o); + pa_source_output_assert_ref(o); c = CONNECTION(o->userdata); pa_assert(c); @@ -440,7 +480,7 @@ static void io_callback(pa_iochannel*io, void *userdata) { static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) { pa_protocol_simple *p = userdata; connection *c = NULL; - char cname[256]; + char cname[256], pname[128]; pa_assert(s); pa_assert(io); @@ -462,12 +502,14 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) c->protocol = p; c->playback.current_memblock = NULL; c->playback.memblock_index = 0; - c->playback.fragment_size = 0; - c->dead = 0; + c->dead = FALSE; + c->playback.underrun = TRUE; pa_atomic_store(&c->playback.missing, 0); - pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname)); + pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); + pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname); pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname)); + pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname); c->client->module = p->module; c->client->kill = client_kill_cb; c->client->userdata = c; @@ -500,21 +542,24 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) c->sink_input->parent.process_msg = sink_input_process_msg; c->sink_input->pop = sink_input_pop_cb; + c->sink_input->process_rewind = sink_input_process_rewind_cb; + c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; c->sink_input->kill = sink_input_kill_cb; c->sink_input->userdata = c; + pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY); + l = (size_t) (pa_bytes_per_second(&p->sample_spec)*PLAYBACK_BUFFER_SECONDS); c->input_memblockq = pa_memblockq_new( 0, l, - 0, + l, pa_frame_size(&p->sample_spec), (size_t) -1, l/PLAYBACK_BUFFER_FRAGMENTS, 0, NULL); - pa_iochannel_socket_set_rcvbuf(io, l/PLAYBACK_BUFFER_FRAGMENTS*5); - c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS; + pa_iochannel_socket_set_rcvbuf(io, l); pa_atomic_store(&c->playback.missing, pa_memblockq_missing(c->input_memblockq)); @@ -551,6 +596,8 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) c->source_output->get_latency = source_output_get_latency_cb; c->source_output->userdata = c; + pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY); + l = (size_t) (pa_bytes_per_second(&p->sample_spec)*RECORD_BUFFER_SECONDS); c->output_memblockq = pa_memblockq_new( 0, @@ -561,7 +608,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) 0, 0, NULL); - pa_iochannel_socket_set_sndbuf(io, l/RECORD_BUFFER_FRAGMENTS*2); + pa_iochannel_socket_set_sndbuf(io, l); pa_source_output_put(c->source_output); } @@ -582,6 +629,7 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv pa_assert(core); pa_assert(server); + pa_assert(m); pa_assert(ma); p = pa_xnew0(pa_protocol_simple, 1); @@ -606,7 +654,7 @@ pa_protocol_simple* pa_protocol_simple_new(pa_core *core, pa_socket_server *serv } p->mode = enable ? RECORD : 0; - enable = 1; + enable = TRUE; if (pa_modargs_get_value_boolean(ma, "playback", &enable) < 0) { pa_log("playback= expects a numeric argument."); goto fail; -- cgit From 94c269e0f4f6c33c0d4f0be66a17504fe1561094 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 00:43:24 +0000 Subject: some fixes to make the esound protocol work on glitch-free again git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2415 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-esound.c | 132 ++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 44 deletions(-) diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c index 388808a5..492dc9fa 100644 --- a/src/pulsecore/protocol-esound.c +++ b/src/pulsecore/protocol-esound.c @@ -70,10 +70,12 @@ #define PLAYBACK_BUFFER_SECONDS (.25) #define PLAYBACK_BUFFER_FRAGMENTS (10) #define RECORD_BUFFER_SECONDS (5) -#define RECORD_BUFFER_FRAGMENTS (100) #define MAX_CACHE_SAMPLE_SIZE (2048000) +#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC) +#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC) + #define SCACHE_PREFIX "esound." /* This is heavily based on esound's code */ @@ -102,8 +104,9 @@ typedef struct connection { struct { pa_memblock *current_memblock; - size_t memblock_index, fragment_size; + size_t memblock_index; pa_atomic_t missing; + pa_bool_t underrun; } playback; struct { @@ -122,7 +125,7 @@ static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject); struct pa_protocol_esound { pa_module *module; pa_core *core; - int public; + pa_bool_t public; pa_socket_server *server; pa_idxset *connections; @@ -150,6 +153,8 @@ typedef struct proto_handler { } esd_proto_handler_info_t; static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk); +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes); +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes); static void sink_input_kill_cb(pa_sink_input *i); static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk); static pa_usec_t source_output_get_latency_cb(pa_source_output *o); @@ -397,8 +402,7 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques CHECK_VALIDITY(sink, "No such sink: %s", c->protocol->sink_name); } - strncpy(name, data, sizeof(name)); - name[sizeof(name)-1] = 0; + pa_strlcpy(name, data, sizeof(name)); utf8_name = pa_utf8_filter(name); pa_client_set_name(c->client, utf8_name); @@ -425,20 +429,23 @@ static int esd_proto_stream_play(connection *c, PA_GCC_UNUSED esd_proto_t reques c->input_memblockq = pa_memblockq_new( 0, l, - 0, + l, pa_frame_size(&ss), (size_t) -1, l/PLAYBACK_BUFFER_FRAGMENTS, 0, NULL); - pa_iochannel_socket_set_rcvbuf(c->io, l/PLAYBACK_BUFFER_FRAGMENTS*2); - c->playback.fragment_size = l/PLAYBACK_BUFFER_FRAGMENTS; + pa_iochannel_socket_set_rcvbuf(c->io, l); c->sink_input->parent.process_msg = sink_input_process_msg; c->sink_input->pop = sink_input_pop_cb; + c->sink_input->process_rewind = sink_input_process_rewind_cb; + c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; c->sink_input->kill = sink_input_kill_cb; c->sink_input->userdata = c; + pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY); + c->state = ESD_STREAMING_DATA; c->protocol->n_player++; @@ -498,8 +505,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi } } - strncpy(name, data, sizeof(name)); - name[sizeof(name)-1] = 0; + pa_strlcpy(name, data, sizeof(name)); utf8_name = pa_utf8_filter(name); pa_client_set_name(c->client, utf8_name); @@ -520,25 +526,27 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0); pa_source_output_new_data_done(&sdata); - CHECK_VALIDITY(c->source_output, "Failed to create source_output."); + CHECK_VALIDITY(c->source_output, "Failed to create source output."); l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS); c->output_memblockq = pa_memblockq_new( 0, l, - 0, + l, pa_frame_size(&ss), 1, 0, 0, NULL); - pa_iochannel_socket_set_sndbuf(c->io, l/RECORD_BUFFER_FRAGMENTS*2); + pa_iochannel_socket_set_sndbuf(c->io, l); c->source_output->push = source_output_push_cb; c->source_output->kill = source_output_kill_cb; c->source_output->get_latency = source_output_get_latency_cb; c->source_output->userdata = c; + pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY); + c->state = ESD_STREAMING_DATA; c->protocol->n_player++; @@ -789,8 +797,7 @@ static int esd_proto_sample_cache(connection *c, PA_GCC_UNUSED esd_proto_t reque CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length); strcpy(name, SCACHE_PREFIX); - strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); - name[sizeof(name)-1] = 0; + pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name."); @@ -822,8 +829,7 @@ static int esd_proto_sample_get_id(connection *c, PA_GCC_UNUSED esd_proto_t requ pa_assert(length == ESD_NAME_MAX); strcpy(name, SCACHE_PREFIX); - strncpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); - name[sizeof(name)-1] = 0; + pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX); CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name."); @@ -1016,6 +1022,7 @@ static int do_read(connection *c) { ssize_t r; size_t l; void *p; + size_t space; pa_assert(c->input_memblockq); @@ -1024,21 +1031,26 @@ static int do_read(connection *c) { if (!(l = pa_atomic_load(&c->playback.missing))) return 0; - if (l > c->playback.fragment_size) - l = c->playback.fragment_size; + if (c->playback.current_memblock) { + + space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index; - if (c->playback.current_memblock) - if (pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index < l) { + if (space <= 0) { pa_memblock_unref(c->playback.current_memblock); c->playback.current_memblock = NULL; - c->playback.memblock_index = 0; } + } if (!c->playback.current_memblock) { - pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, c->playback.fragment_size*2)); + pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, 0)); c->playback.memblock_index = 0; + + space = pa_memblock_get_length(c->playback.current_memblock); } + if (l > space) + l = space; + p = pa_memblock_acquire(c->playback.current_memblock); r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l); pa_memblock_release(c->playback.current_memblock); @@ -1126,12 +1138,11 @@ static void do_work(connection *c) { if (c->dead) return; - if (pa_iochannel_is_readable(c->io)) { + if (pa_iochannel_is_readable(c->io)) if (do_read(c) < 0) goto fail; - } - if (c->state == ESD_STREAMING_DATA && c->source_output && pa_iochannel_is_hungup(c->io)) + if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io)) /* In case we are in capture mode we will never call read() * on the socket, hence we need to detect the hangup manually * here, instead of simply waiting for read() to return 0. */ @@ -1216,15 +1227,19 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int /* New data from the main loop */ pa_memblockq_push_align(c->input_memblockq, chunk); + if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) { + pa_log_debug("Requesting rewind due to end of underrun."); + pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE); + } + /* pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */ return 0; } - case SINK_INPUT_MESSAGE_DISABLE_PREBUF: { + case SINK_INPUT_MESSAGE_DISABLE_PREBUF: pa_memblockq_prebuf_disable(c->input_memblockq); return 0; - } case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = userdata; @@ -1243,32 +1258,60 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int /* Called from thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { connection*c; - int r; - pa_assert(i); + pa_sink_input_assert_ref(i); c = CONNECTION(i->userdata); connection_assert_ref(c); pa_assert(chunk); - if ((r = pa_memblockq_peek(c->input_memblockq, chunk)) < 0) { + if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) { + c->playback.underrun = TRUE; if (c->dead && pa_sink_input_safe_to_remove(i)) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL); + + return -1; } else { - size_t old, new; + size_t m; + + c->playback.underrun = FALSE; - old = pa_memblockq_missing(c->input_memblockq); pa_memblockq_drop(c->input_memblockq, chunk->length); - new = pa_memblockq_missing(c->input_memblockq); + m = pa_memblockq_pop_missing(c->input_memblockq); - if (new > old) { - if (pa_atomic_add(&c->playback.missing, new - old) <= 0) + if (m > 0) + if (pa_atomic_add(&c->playback.missing, m) <= 0) pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL); - } + + return 0; } +} + +/* Called from thread context */ +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + connection *c; - return r; + pa_sink_input_assert_ref(i); + c = CONNECTION(i->userdata); + connection_assert_ref(c); + + /* If we are in an underrun, then we don't rewind */ + if (i->thread_info.underrun_for > 0) + return; + + pa_memblockq_rewind(c->input_memblockq, nbytes); +} + +/* Called from thread context */ +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + connection *c; + + pa_sink_input_assert_ref(i); + c = CONNECTION(i->userdata); + connection_assert_ref(c); + + pa_memblockq_set_maxrewind(c->input_memblockq, nbytes); } static void sink_input_kill_cb(pa_sink_input *i) { @@ -1283,7 +1326,7 @@ static void sink_input_kill_cb(pa_sink_input *i) { static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { connection *c; - pa_assert(o); + pa_source_output_assert_ref(o); c = CONNECTION(o->userdata); pa_assert(c); pa_assert(chunk); @@ -1300,7 +1343,7 @@ static void source_output_kill_cb(pa_source_output *o) { static pa_usec_t source_output_get_latency_cb(pa_source_output *o) { connection*c; - pa_assert(o); + pa_source_output_assert_ref(o); c = CONNECTION(o->userdata); pa_assert(c); @@ -1346,6 +1389,7 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname); c->client = pa_client_new(p->core, __FILE__, cname); + pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname); c->client->module = p->module; c->client->kill = client_kill_cb; c->client->userdata = c; @@ -1371,11 +1415,10 @@ static void on_connection(pa_socket_server*s, pa_iochannel *io, void *userdata) c->playback.current_memblock = NULL; c->playback.memblock_index = 0; - c->playback.fragment_size = 0; + c->playback.underrun = TRUE; pa_atomic_store(&c->playback.missing, 0); - c->scache.memchunk.length = c->scache.memchunk.index = 0; - c->scache.memchunk.memblock = NULL; + pa_memchunk_reset(&c->scache.memchunk); c->scache.name = NULL; c->original_name = NULL; @@ -1456,7 +1499,8 @@ void pa_protocol_esound_free(pa_protocol_esound *p) { connection_unlink(c); pa_idxset_free(p->connections, NULL, NULL); - pa_socket_server_unref(p->server); + if (p->server) + pa_socket_server_unref(p->server); if (p->auth_ip_acl) pa_ip_acl_free(p->auth_ip_acl); -- cgit From dd29f677034c71024d703ebe45fcd7404808ec7d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 01:00:35 +0000 Subject: fix braindead mistake git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2416 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/protocol-native.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index ee7e1ed8..2adcdfc7 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -3919,7 +3919,7 @@ static void on_connection(PA_GCC_UNUSED pa_socket_server*s, pa_iochannel *io, vo pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname)); pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname); c->client = pa_client_new(p->core, __FILE__, cname); - pa_proplist_sets(c->client, "native-protocol.peer", pname); + pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname); c->client->kill = client_kill_cb; c->client->userdata = c; c->client->module = p->module; -- cgit From 8baa1a46e3a0995e2609bad7052676f9e86dd9a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 01:58:24 +0000 Subject: fix pipe sink for glitch-free git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2417 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-null-sink.c | 2 + src/modules/module-pipe-sink.c | 97 ++++++++++++++++++++++++++---------------- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index 606b87d0..ded4df98 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -160,6 +160,8 @@ static void process_render(struct userdata *u, pa_usec_t now) { size_t nbytes; size_t ate = 0; + pa_assert(u); + /* This is the configured latency. Sink inputs connected to us might not have a single frame more than this value queued. Hence: at maximum read this many bytes from the sink inputs. */ diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index 870b32ff..f172e200 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -62,7 +62,7 @@ PA_MODULE_USAGE( "rate=" "channel_map="); -#define DEFAULT_FILE_NAME "/tmp/music.output" +#define DEFAULT_FILE_NAME "fifo_output" #define DEFAULT_SINK_NAME "fifo_output" struct userdata { @@ -80,6 +80,8 @@ struct userdata { pa_memchunk memchunk; pa_rtpoll_item *rtpoll_item; + + int write_type; }; static const char* const valid_modargs[] = { @@ -109,16 +111,64 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse n += u->memchunk.length; *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->sink->sample_spec); - break; + return 0; } } return pa_sink_process_msg(o, code, data, offset, chunk); } +static void process_rewind(struct userdata *u) { + pa_assert(u); + + pa_log_debug("Rewind requested but not supported by pipe sink. Ignoring."); + u->sink->thread_info.rewind_nbytes = 0; +} + +static int process_render(struct userdata *u) { + pa_assert(u); + + if (u->memchunk.length <= 0) + pa_sink_render(u->sink, PIPE_BUF, &u->memchunk); + + pa_assert(u->memchunk.length > 0); + + for (;;) { + ssize_t l; + void *p; + + p = pa_memblock_acquire(u->memchunk.memblock); + l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &u->write_type); + pa_memblock_release(u->memchunk.memblock); + + pa_assert(l != 0); + + if (l < 0) { + + if (errno == EINTR) + continue; + else if (errno != EAGAIN) { + pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); + return -1; + } + + } else { + + u->memchunk.index += l; + u->memchunk.length -= l; + + if (u->memchunk.length <= 0) { + pa_memblock_unref(u->memchunk.memblock); + pa_memchunk_reset(&u->memchunk); + } + } + + return 0; + } +} + static void thread_func(void *userdata) { struct userdata *u = userdata; - int write_type = 0; pa_assert(u); @@ -134,39 +184,14 @@ static void thread_func(void *userdata) { pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Render some data and write it to the fifo */ - if (u->sink->thread_info.state == PA_SINK_RUNNING && pollfd->revents) { - ssize_t l; - void *p; + if (u->sink->thread_info.state == PA_SINK_RUNNING) { - if (u->memchunk.length <= 0) - pa_sink_render(u->sink, PIPE_BUF, &u->memchunk); + if (u->sink->thread_info.rewind_nbytes > 0) + process_rewind(u); - pa_assert(u->memchunk.length > 0); - - p = pa_memblock_acquire(u->memchunk.memblock); - l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); - pa_memblock_release(u->memchunk.memblock); - - pa_assert(l != 0); - - if (l < 0) { - - if (errno == EINTR) - continue; - else if (errno != EAGAIN) { - pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); + if (pollfd->revents) { + if (process_render(u) < 0) goto fail; - } - - } else { - - u->memchunk.index += l; - u->memchunk.length -= l; - - if (u->memchunk.length <= 0) { - pa_memblock_unref(u->memchunk.memblock); - pa_memchunk_reset(&u->memchunk); - } pollfd->revents = 0; } @@ -229,8 +254,9 @@ int pa__init(pa_module*m) { pa_memchunk_reset(&u->memchunk); u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); + u->write_type = 0; - u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME)); + u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME)); mkfifo(u->filename, 0666); if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) { @@ -256,8 +282,7 @@ int pa__init(pa_module*m) { data.module = m; pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Unix FIFO sink %s", u->filename)); - pa_xfree(t); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO sink %s", u->filename); pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); -- cgit From 9c48ed116c33e4f3ec559e8e479e4b948bcd4e1a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 02:03:01 +0000 Subject: update pipe source for glitch-free, too git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2418 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-pipe-sink.c | 1 - src/modules/module-pipe-source.c | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c index f172e200..cc648928 100644 --- a/src/modules/module-pipe-sink.c +++ b/src/modules/module-pipe-sink.c @@ -230,7 +230,6 @@ int pa__init(pa_module*m) { pa_sample_spec ss; pa_channel_map map; pa_modargs *ma; - char *t; struct pollfd *pollfd; pa_sink_new_data data; diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index 5bf4da1f..c2f464b8 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -160,6 +160,7 @@ static void thread_func(void *userdata) { goto finish; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); + if (pollfd->revents & ~POLLIN) { pa_log("FIFO shutdown."); goto fail; @@ -182,7 +183,6 @@ int pa__init(pa_module*m) { pa_sample_spec ss; pa_channel_map map; pa_modargs *ma; - char *t; struct pollfd *pollfd; pa_source_new_data data; @@ -207,7 +207,7 @@ int pa__init(pa_module*m) { u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); - u->filename = pa_xstrdup(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME)); + u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME)); mkfifo(u->filename, 0666); if ((u->fd = open(u->filename, O_RDWR|O_NOCTTY)) < 0) { @@ -233,8 +233,7 @@ int pa__init(pa_module*m) { data.module = m; pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Unix FIFO source %s", u->filename)); - pa_xfree(t); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename); pa_source_new_data_set_sample_spec(&data, &ss); pa_source_new_data_set_channel_map(&data, &map); -- cgit From 2eca8c9496e1fb66aa8b041f91a1dcb026c5818f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 02:35:50 +0000 Subject: don't spam us with wakeup msgs in non-tsched mode git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2419 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 97e1e59f..9c4da07a 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -1058,7 +1058,7 @@ static void thread_func(void *userdata) { u->since_start = 0; } - if (revents) + if (revents && u->use_tsched) pa_log_debug("Wakeup from ALSA! (%i)", revents); } } -- cgit From 787b8696943ba3683486a9efcf729cb92e70ce39 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 02:36:38 +0000 Subject: initialize volume properly, set more properties, modernizations git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2420 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-oss.c | 109 ++++++++++++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 45 deletions(-) diff --git a/src/modules/module-oss.c b/src/modules/module-oss.c index 3dd4508e..cf7584db 100644 --- a/src/modules/module-oss.c +++ b/src/modules/module-oss.c @@ -1146,6 +1146,8 @@ int pa__init(pa_module*m) { char hwdesc[64]; const char *name; pa_bool_t namereg_fail; + pa_sink_new_data sink_new_data; + pa_source_new_data source_new_data; pa_assert(m); @@ -1226,8 +1228,8 @@ int pa__init(pa_module*m) { m->userdata = u; u->fd = fd; u->mixer_fd = -1; - u->use_getospace = u->use_getispace = 1; - u->use_getodelay = 1; + u->use_getospace = u->use_getispace = TRUE; + u->use_getodelay = TRUE; u->mode = mode; u->frame_size = pa_frame_size(&ss); u->device_name = pa_xstrdup(dev); @@ -1243,27 +1245,26 @@ int pa__init(pa_module*m) { pa_log_info("Input -- %u fragments of size %u.", info.fragstotal, info.fragsize); u->in_fragment_size = info.fragsize; u->in_nfrags = info.fragstotal; - u->use_getispace = 1; + u->use_getispace = TRUE; } if (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { pa_log_info("Output -- %u fragments of size %u.", info.fragstotal, info.fragsize); u->out_fragment_size = info.fragsize; u->out_nfrags = info.fragstotal; - u->use_getospace = 1; + u->use_getospace = TRUE; } u->in_hwbuf_size = u->in_nfrags * u->in_fragment_size; u->out_hwbuf_size = u->out_nfrags * u->out_fragment_size; if (mode != O_WRONLY) { - pa_source_new_data data; char *name_buf = NULL; if (use_mmap) { if ((u->in_mmap = mmap(NULL, u->in_hwbuf_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { pa_log_warn("mmap(PROT_READ) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno)); - use_mmap = u->use_mmap = 0; + use_mmap = u->use_mmap = FALSE; u->in_mmap = NULL; } else pa_log_debug("Successfully mmap()ed input buffer."); @@ -1276,20 +1277,22 @@ int pa__init(pa_module*m) { namereg_fail = FALSE; } - pa_source_new_data_init(&data); - data.driver = __FILE__; - data.module = m; - pa_source_new_data_set_name(&data, name); - data.namereg_fail = namereg_fail; - pa_source_new_data_set_sample_spec(&data, &ss); - pa_source_new_data_set_channel_map(&data, &map); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, dev); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "oss"); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial"); - - u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); - pa_source_new_data_done(&data); + pa_source_new_data_init(&source_new_data); + source_new_data.driver = __FILE__; + source_new_data.module = m; + pa_source_new_data_set_name(&source_new_data, name); + source_new_data.namereg_fail = namereg_fail; + pa_source_new_data_set_sample_spec(&source_new_data, &ss); + pa_source_new_data_set_channel_map(&source_new_data, &map); + pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_STRING, dev); + pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_API, "oss"); + pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev); + pa_proplist_sets(source_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial"); + pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->in_hwbuf_size)); + pa_proplist_setf(source_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->in_fragment_size)); + + u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); + pa_source_new_data_done(&source_new_data); pa_xfree(name_buf); if (!u->source) { @@ -1309,7 +1312,6 @@ int pa__init(pa_module*m) { } if (mode != O_RDONLY) { - pa_sink_new_data data; char *name_buf = NULL; if (use_mmap) { @@ -1320,7 +1322,7 @@ int pa__init(pa_module*m) { goto go_on; } else { pa_log_warn("mmap(PROT_WRITE) failed, reverting to non-mmap mode: %s", pa_cstrerror(errno)); - u->use_mmap = (use_mmap = FALSE); + u->use_mmap = use_mmap = FALSE; u->out_mmap = NULL; } } else { @@ -1330,26 +1332,28 @@ int pa__init(pa_module*m) { } if ((name = pa_modargs_get_value(ma, "sink_name", NULL))) - namereg_fail = 1; + namereg_fail = TRUE; else { name = name_buf = pa_sprintf_malloc("oss_output.%s", pa_path_get_filename(dev)); - namereg_fail = 0; + namereg_fail = FALSE; } - pa_sink_new_data_init(&data); - data.driver = __FILE__; - data.module = m; - pa_sink_new_data_set_name(&data, name); - data.namereg_fail = namereg_fail; - pa_sink_new_data_set_sample_spec(&data, &ss); - pa_sink_new_data_set_channel_map(&data, &map); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, dev); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "oss"); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial"); - - u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); - pa_sink_new_data_done(&data); + pa_sink_new_data_init(&sink_new_data); + sink_new_data.driver = __FILE__; + sink_new_data.module = m; + pa_sink_new_data_set_name(&sink_new_data, name); + sink_new_data.namereg_fail = namereg_fail; + pa_sink_new_data_set_sample_spec(&sink_new_data, &ss); + pa_sink_new_data_set_channel_map(&sink_new_data, &map); + pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_STRING, dev); + pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_API, "oss"); + pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, hwdesc[0] ? hwdesc : dev); + pa_proplist_sets(sink_new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, use_mmap ? "mmap" : "serial"); + pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (u->out_hwbuf_size)); + pa_proplist_setf(sink_new_data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (u->out_fragment_size)); + + u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY); + pa_sink_new_data_done(&sink_new_data); pa_xfree(name_buf); if (!u->sink) { @@ -1369,7 +1373,7 @@ int pa__init(pa_module*m) { } if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) { - int do_close = 1; + pa_bool_t do_close = TRUE; u->mixer_devmask = 0; if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0) @@ -1381,7 +1385,7 @@ int pa__init(pa_module*m) { u->sink->flags |= PA_SINK_HW_VOLUME_CTRL; u->sink->get_volume = sink_get_volume; u->sink->set_volume = sink_set_volume; - do_close = 0; + do_close = FALSE; } if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) { @@ -1389,7 +1393,7 @@ int pa__init(pa_module*m) { u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL; u->source->get_volume = source_get_volume; u->source->set_volume = source_set_volume; - do_close = 0; + do_close = FALSE; } } @@ -1411,10 +1415,25 @@ go_on: } /* Read mixer settings */ - if (u->sink && u->sink->get_volume) - sink_get_volume(u->sink); - if (u->source && u->source->get_volume) - source_get_volume(u->source); + if (u->sink) { + if (sink_new_data.volume_is_set) { + if (u->sink->set_volume) + u->sink->set_volume(u->sink); + } else { + if (u->sink->get_volume) + u->sink->get_volume(u->sink); + } + } + + if (u->source) { + if (source_new_data.volume_is_set) { + if (u->source->set_volume) + u->source->set_volume(u->source); + } else { + if (u->source->get_volume) + u->source->get_volume(u->source); + } + } if (u->sink) pa_sink_put(u->sink); -- cgit From 37813d97452095b87cb160a21f52b9bdea5c50fb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 02:44:38 +0000 Subject: modernizations git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2421 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-jack-sink.c | 6 ++---- src/modules/module-jack-source.c | 4 +--- src/modules/module-null-sink.c | 2 +- src/modules/module-pipe-source.c | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c index f97041a8..1ef5d235 100644 --- a/src/modules/module-jack-sink.c +++ b/src/modules/module-jack-sink.c @@ -53,7 +53,7 @@ /* General overview: * - * Because JACK has a very unflexible event loop management, which + * Because JACK has a very unflexible event loop management which * doesn't allow us to add our own event sources to the event thread * we cannot use the JACK real-time thread for dispatching our PA * work. Instead, we run an additional RT thread which does most of @@ -276,7 +276,6 @@ int pa__init(pa_module*m) { pa_bool_t do_connect = TRUE; unsigned i; const char **ports = NULL, **p; - char *t; pa_sink_new_data data; pa_assert(m); @@ -364,8 +363,7 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); if (server_name) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Jack sink (%s)", jack_get_client_name(u->client))); - pa_xfree(t); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack sink (%s)", jack_get_client_name(u->client)); pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY); diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c index 18ffa55c..fa2ec5eb 100644 --- a/src/modules/module-jack-source.c +++ b/src/modules/module-jack-source.c @@ -253,7 +253,6 @@ int pa__init(pa_module*m) { pa_bool_t do_connect = TRUE; unsigned i; const char **ports = NULL, **p; - char *t; pa_source_new_data data; pa_assert(m); @@ -335,8 +334,7 @@ int pa__init(pa_module*m) { pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "jack"); if (server_name) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server_name); - pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, t = pa_sprintf_malloc("Jack source (%s)", jack_get_client_name(u->client))); - pa_xfree(t); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Jack source (%s)", jack_get_client_name(u->client)); pa_proplist_sets(data.proplist, "jack.client_name", jack_get_client_name(u->client)); u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY); diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c index ded4df98..aff244fa 100644 --- a/src/modules/module-null-sink.c +++ b/src/modules/module-null-sink.c @@ -219,7 +219,7 @@ static void thread_func(void *userdata) { pa_rtpoll_set_timer_disabled(u->rtpoll); /* Hmm, nothing to do. Let's sleep */ - if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) + if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c index c2f464b8..83eb4f8a 100644 --- a/src/modules/module-pipe-source.c +++ b/src/modules/module-pipe-source.c @@ -153,7 +153,7 @@ static void thread_func(void *userdata) { /* Hmm, nothing to do. Let's sleep */ pollfd->events = u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0; - if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) + if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) -- cgit From df73688440230ac62e7e4e190a99194274473029 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 14 May 2008 17:13:28 +0000 Subject: modernizations git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2422 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-esound-sink.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c index 4aa7d674..87b87c3d 100644 --- a/src/modules/module-esound-sink.c +++ b/src/modules/module-esound-sink.c @@ -294,7 +294,7 @@ static void thread_func(void *userdata) { } /* Hmm, nothing to do. Let's sleep */ - pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0; + pollfd->events = PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0; } if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) @@ -502,10 +502,8 @@ static void on_connection(PA_GCC_UNUSED pa_socket_client *c, pa_iochannel*io, vo int pa__init(pa_module*m) { struct userdata *u = NULL; - const char *p; pa_sample_spec ss; pa_modargs *ma = NULL; - char *t; const char *espeaker; uint32_t key; pa_sink_new_data data; @@ -554,11 +552,18 @@ int pa__init(pa_module*m) { u->state = STATE_AUTH; u->latency = 0; + if (!(espeaker = getenv("ESPEAKER"))) + espeaker = ESD_UNIX_SOCKET_NAME; + + espeaker = pa_modargs_get_value(ma, "server", espeaker); + pa_sink_new_data_init(&data); data.driver = __FILE__; data.module = m; pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_sink_new_data_set_sample_spec(&data, &ss); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, espeaker); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Esound sink '%s'", espeaker); u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK); pa_sink_new_data_done(&data); @@ -574,17 +579,11 @@ int pa__init(pa_module*m) { pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); - if (!(espeaker = getenv("ESPEAKER"))) - espeaker = ESD_UNIX_SOCKET_NAME; - - if (!(u->client = pa_socket_client_new_string(u->core->mainloop, p = pa_modargs_get_value(ma, "server", espeaker), ESD_DEFAULT_PORT))) { + if (!(u->client = pa_socket_client_new_string(u->core->mainloop, espeaker, ESD_DEFAULT_PORT))) { pa_log("Failed to connect to server."); goto fail; } - pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Esound sink '%s'", p)); - pa_xfree(t); - pa_socket_client_set_callback(u->client, on_connection, u); /* Prepare the initial request */ -- cgit From 103ceaa94488516b0ce0f7b2d428348469db43ce Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 17:57:52 +0000 Subject: add pa_memblockq_get_nblocks() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2423 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/memblockq.c | 6 ++++++ src/pulsecore/memblockq.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c index 13eb1101..c047e56f 100644 --- a/src/pulsecore/memblockq.c +++ b/src/pulsecore/memblockq.c @@ -902,3 +902,9 @@ void pa_memblockq_silence(pa_memblockq *bq) { pa_assert(bq->n_blocks == 0); } + +unsigned pa_memblockq_get_nblocks(pa_memblockq *bq) { + pa_assert(bq); + + return bq->n_blocks; +} diff --git a/src/pulsecore/memblockq.h b/src/pulsecore/memblockq.h index a6065dea..7c38757f 100644 --- a/src/pulsecore/memblockq.h +++ b/src/pulsecore/memblockq.h @@ -167,4 +167,6 @@ void pa_memblockq_silence(pa_memblockq *bq); /* Check whether we currently are in prebuf state */ pa_bool_t pa_memblockq_prebuf_active(pa_memblockq *bq); +unsigned pa_memblockq_get_nblocks(pa_memblockq *bq); + #endif -- cgit From 689528045e63843040ea2db24bc027224b342368 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 17:58:11 +0000 Subject: add pa_ulog2() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2424 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/core-util.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index 49315b55..e9313d4b 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -155,6 +155,17 @@ static inline unsigned pa_make_power_of_two(unsigned n) { return n + 1; } +static inline unsigned pa_ulog2(unsigned n) { + unsigned r = 0; + + while (n) { + r++; + n = n >> 1; + } + + return r; +} + void pa_close_pipe(int fds[2]); char *pa_readlink(const char *p); -- cgit From 1b7157a8d64ae4a389346805cb7a74c4bf50c19c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 17:58:26 +0000 Subject: add PA_REFCNT_INIT_ZERO git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2425 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/refcnt.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pulsecore/refcnt.h b/src/pulsecore/refcnt.h index 64271ab2..f0885fb4 100644 --- a/src/pulsecore/refcnt.h +++ b/src/pulsecore/refcnt.h @@ -32,6 +32,9 @@ #define PA_REFCNT_INIT(p) \ pa_atomic_store(&(p)->_ref, 1) +#define PA_REFCNT_INIT_ZERO(p) \ + pa_atomic_store(&(p)->_ref, 0) + #define PA_REFCNT_INC(p) \ pa_atomic_inc(&(p)->_ref) -- cgit From 076ffa340a05965f12713b8ea0bb78313c066c7c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 17:58:53 +0000 Subject: add 'stream' as media role git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2426 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/proplist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index d1bf371f..f433ec62 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -38,7 +38,7 @@ PA_C_DECL_BEGIN * media.filename * media.icon * media.icon_name - * media.role video, music, game, event, phone, production, filter, abstract + * media.role video, music, game, event, phone, production, filter, abstract, stream * event.id button-click, session-login * event.x11.display * event.x11.xid -- cgit From c801d089cd7c6248dc362278fe6212298448ffa9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 17:59:12 +0000 Subject: use pa_bool_t git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2427 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/rtp/sdp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/rtp/sdp.c b/src/modules/rtp/sdp.c index 50ac157a..9265a200 100644 --- a/src/modules/rtp/sdp.c +++ b/src/modules/rtp/sdp.c @@ -117,7 +117,7 @@ static pa_sample_spec *parse_sdp_sample_spec(pa_sample_spec *ss, char *c) { pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { uint16_t port = 0; - int ss_valid = 0; + pa_bool_t ss_valid = FALSE; pa_assert(t); pa_assert(i); @@ -202,7 +202,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { i->payload = (uint8_t) _payload; if (pa_rtp_sample_spec_from_payload(i->payload, &i->sample_spec)) - ss_valid = 1; + ss_valid = TRUE; } } } else if (pa_startswith(t, "a=rtpmap:")) { @@ -222,7 +222,7 @@ pa_sdp_info *pa_sdp_parse(const char *t, pa_sdp_info *i, int is_goodbye) { c[strcspn(c, "\n")] = 0; if (parse_sdp_sample_spec(&i->sample_spec, c)) - ss_valid = 1; + ss_valid = TRUE; } } } -- cgit From d10ee7d6e2ddeebf4308aa4fcd049dac9c4ddf02 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 17:59:42 +0000 Subject: more pa_bool_t'ization git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2428 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/rtp/sap.c | 4 ++-- src/modules/rtp/sap.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c index ed7eb0be..123bc494 100644 --- a/src/modules/rtp/sap.c +++ b/src/modules/rtp/sap.c @@ -71,7 +71,7 @@ void pa_sap_context_destroy(pa_sap_context *c) { pa_xfree(c->sdp_data); } -int pa_sap_send(pa_sap_context *c, int goodbye) { +int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye) { uint32_t header; struct sockaddr_storage sa_buf; struct sockaddr *sa = (struct sockaddr*) &sa_buf; @@ -127,7 +127,7 @@ pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd) { return c; } -int pa_sap_recv(pa_sap_context *c, int *goodbye) { +int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye) { struct msghdr m; struct iovec iov; int size, k; diff --git a/src/modules/rtp/sap.h b/src/modules/rtp/sap.h index f906a32b..db096d61 100644 --- a/src/modules/rtp/sap.h +++ b/src/modules/rtp/sap.h @@ -40,9 +40,9 @@ typedef struct pa_sap_context { pa_sap_context* pa_sap_context_init_send(pa_sap_context *c, int fd, char *sdp_data); void pa_sap_context_destroy(pa_sap_context *c); -int pa_sap_send(pa_sap_context *c, int goodbye); +int pa_sap_send(pa_sap_context *c, pa_bool_t goodbye); pa_sap_context* pa_sap_context_init_recv(pa_sap_context *c, int fd); -int pa_sap_recv(pa_sap_context *c, int *goodbye); +int pa_sap_recv(pa_sap_context *c, pa_bool_t *goodbye); #endif -- cgit From 2bc77ff49aa06eb87645d493cb345a41bab69b19 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 18:00:31 +0000 Subject: reduce number of allocated memblocks when receiving RTP data by reusing blocks git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2429 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/rtp/rtp.c | 44 +++++++++++++++++++++++++++++++++++++------- src/modules/rtp/rtp.h | 2 ++ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c index 997fcc34..5c299844 100644 --- a/src/modules/rtp/rtp.c +++ b/src/modules/rtp/rtp.c @@ -55,6 +55,8 @@ pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssr c->payload = payload & 127; c->frame_size = frame_size; + pa_memchunk_reset(&c->memchunk); + return c; } @@ -152,6 +154,8 @@ pa_rtp_context* pa_rtp_context_init_recv(pa_rtp_context *c, int fd, size_t frame c->fd = fd; c->frame_size = frame_size; + + pa_memchunk_reset(&c->memchunk); return c; } @@ -173,12 +177,28 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) { goto fail; } - if (!size) + if (size <= 0) return 0; - chunk->memblock = pa_memblock_new(pool, size); + if (c->memchunk.length < (unsigned) size) { + size_t l; + + if (c->memchunk.memblock) + pa_memblock_unref(c->memchunk.memblock); + + l = PA_MAX((size_t) size, pa_mempool_block_size_max(pool)); + + c->memchunk.memblock = pa_memblock_new(pool, l); + c->memchunk.index = 0; + c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock); + } + + pa_assert(c->memchunk.length >= (size_t) size); - iov.iov_base = pa_memblock_acquire(chunk->memblock); + chunk->memblock = pa_memblock_ref(c->memchunk.memblock); + chunk->index = c->memchunk.index; + + iov.iov_base = (uint8_t*) pa_memblock_acquire(chunk->memblock) + chunk->index; iov.iov_len = size; m.msg_name = NULL; @@ -236,14 +256,22 @@ int pa_rtp_recv(pa_rtp_context *c, pa_memchunk *chunk, pa_mempool *pool) { goto fail; } - chunk->index = 12 + cc*4; - chunk->length = size - chunk->index; + chunk->index += 12 + cc*4; + chunk->length = size - 12 + cc*4; if (chunk->length % c->frame_size != 0) { pa_log_warn("Bad RTP packet size."); goto fail; } + c->memchunk.index = chunk->index + chunk->length; + c->memchunk.length = pa_memblock_get_length(c->memchunk.memblock) - c->memchunk.index; + + if (c->memchunk.length <= 0) { + pa_memblock_unref(c->memchunk.memblock); + pa_memchunk_reset(&c->memchunk); + } + return 0; fail: @@ -329,7 +357,10 @@ int pa_rtp_sample_spec_valid(const pa_sample_spec *ss) { void pa_rtp_context_destroy(pa_rtp_context *c) { pa_assert(c); - pa_close(c->fd); + pa_assert_se(pa_close(c->fd) == 0); + + if (c->memchunk.memblock) + pa_memblock_unref(c->memchunk.memblock); } const char* pa_rtp_format_to_string(pa_sample_format_t f) { @@ -361,4 +392,3 @@ pa_sample_format_t pa_rtp_string_to_format(const char *s) { else return PA_SAMPLE_INVALID; } - diff --git a/src/modules/rtp/rtp.h b/src/modules/rtp/rtp.h index ad7175ca..a366d7a6 100644 --- a/src/modules/rtp/rtp.h +++ b/src/modules/rtp/rtp.h @@ -37,6 +37,8 @@ typedef struct pa_rtp_context { uint32_t ssrc; uint8_t payload; size_t frame_size; + + pa_memchunk memchunk; } pa_rtp_context; pa_rtp_context* pa_rtp_context_init_send(pa_rtp_context *c, int fd, uint32_t ssrc, uint8_t payload, size_t frame_size); -- cgit From 70c5967806da744dc2a3c121d89043dcceb4a260 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 20:38:29 +0000 Subject: increase shm size limit, modernizations git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2430 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/shm.c | 12 ++++++------ src/pulsecore/shm.h | 8 +++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c index a1bbf609..a6843819 100644 --- a/src/pulsecore/shm.c +++ b/src/pulsecore/shm.c @@ -57,7 +57,7 @@ #define MADV_REMOVE 9 #endif -#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*20)) +#define MAX_SHM_SIZE (PA_ALIGN(1024*1024*64)) #ifdef __linux__ /* On Linux we know that the shared memory blocks are files in @@ -86,13 +86,13 @@ static char *segment_name(char *fn, size_t l, unsigned id) { return fn; } -int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) { +int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode) { char fn[32]; int fd = -1; pa_assert(m); pa_assert(size > 0); - pa_assert(size < MAX_SHM_SIZE); + pa_assert(size <= MAX_SHM_SIZE); pa_assert(mode >= 0600); /* Each time we create a new SHM area, let's first drop all stale @@ -124,7 +124,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) { m->ptr = pa_xmalloc(m->size); #endif - m->do_unlink = 0; + m->do_unlink = FALSE; } else { #ifdef HAVE_SHM_OPEN @@ -157,7 +157,7 @@ int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode) { pa_atomic_store(&marker->marker, SHM_MARKER); pa_assert_se(close(fd) == 0); - m->do_unlink = 1; + m->do_unlink = TRUE; #else return -1; #endif @@ -375,7 +375,7 @@ int pa_shm_cleanup(void) { /* Ok, the owner of this shms segment is dead, so, let's remove the segment */ segment_name(fn, sizeof(fn), id); - if (shm_unlink(fn) < 0 && errno != EACCES) + if (shm_unlink(fn) < 0 && errno != EACCES && errno != ENOENT) pa_log_warn("Failed to remove SHM segment %s: %s\n", fn, pa_cstrerror(errno)); } diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h index 270591de..60bc355f 100644 --- a/src/pulsecore/shm.h +++ b/src/pulsecore/shm.h @@ -26,15 +26,17 @@ #include +#include + typedef struct pa_shm { unsigned id; void *ptr; size_t size; - int do_unlink; - int shared; + pa_bool_t do_unlink:1; + pa_bool_t shared:1; } pa_shm; -int pa_shm_create_rw(pa_shm *m, size_t size, int shared, mode_t mode); +int pa_shm_create_rw(pa_shm *m, size_t size, pa_bool_t shared, mode_t mode); int pa_shm_attach_ro(pa_shm *m, unsigned id); void pa_shm_punch(pa_shm *m, size_t offset, size_t size); -- cgit From f96a8adb0cd0679ddc45711c1d943ccc13ea764b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 20:39:46 +0000 Subject: increase default mempool size, make mempool_slot an abstract struct because the only fields it defined where actually unused git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2431 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/memblock.c | 54 ++++++++++++++++++++---------------------------- src/pulsecore/memblock.h | 2 +- 2 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c index a5552786..7005b441 100644 --- a/src/pulsecore/memblock.c +++ b/src/pulsecore/memblock.c @@ -46,8 +46,12 @@ #include "memblock.h" -#define PA_MEMPOOL_SLOTS_MAX 512 -#define PA_MEMPOOL_SLOT_SIZE (32*1024) +/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please + * note that the footprint is usually much smaller, since the data is + * stored in SHM and our OS does not commit the memory before we use + * it for the first time. */ +#define PA_MEMPOOL_SLOTS_MAX 1024 +#define PA_MEMPOOL_SLOT_SIZE (64*1024) #define PA_MEMEXPORT_SLOTS_MAX 128 @@ -127,11 +131,6 @@ struct pa_memexport { PA_LLIST_FIELDS(pa_memexport); }; -struct mempool_slot { - PA_LLIST_FIELDS(struct mempool_slot); - /* the actual data follows immediately hereafter */ -}; - struct pa_mempool { pa_semaphore *semaphore; pa_mutex *mutex; @@ -204,9 +203,7 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) { pa_memblock *b; pa_assert(p); - - if (length <= 0) - length = pa_mempool_block_size_max(p); + pa_assert(length); if (!(b = pa_memblock_new_pool(p, length))) b = memblock_new_appended(p, length); @@ -219,12 +216,12 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) { pa_memblock *b; pa_assert(p); - pa_assert(length > 0); + pa_assert(length); /* If -1 is passed as length we choose the size for the caller. */ if (length == (size_t) -1) - length = p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock)); + length = p->block_size - PA_ALIGN(sizeof(pa_memblock)); b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length); PA_REFCNT_INIT(b); @@ -265,11 +262,9 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) { return slot; } -/* No lock necessary */ -static void* mempool_slot_data(struct mempool_slot *slot) { - pa_assert(slot); - - return (uint8_t*) slot + PA_ALIGN(sizeof(struct mempool_slot)); +/* No lock necessary, totally redundant anyway */ +static inline void* mempool_slot_data(struct mempool_slot *slot) { + return slot; } /* No lock necessary */ @@ -298,7 +293,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { struct mempool_slot *slot; pa_assert(p); - pa_assert(length > 0); + pa_assert(length); /* If -1 is passed as length we choose the size for the caller: we * take the largest size that fits in one of our slots. */ @@ -306,7 +301,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { if (length == (size_t) -1) length = pa_mempool_block_size_max(p); - if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= PA_ALIGN(sizeof(pa_memblock)) + length) { + if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) { if (!(slot = mempool_allocate_slot(p))) return NULL; @@ -315,7 +310,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { b->type = PA_MEMBLOCK_POOL; pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); - } else if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= length) { + } else if (p->block_size >= length) { if (!(slot = mempool_allocate_slot(p))) return NULL; @@ -327,7 +322,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { pa_atomic_ptr_store(&b->data, mempool_slot_data(slot)); } else { - pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)))); + pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size); pa_atomic_inc(&p->stat.n_too_large_for_pool); return NULL; } @@ -350,7 +345,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_boo pa_assert(p); pa_assert(d); pa_assert(length != (size_t) -1); - pa_assert(length > 0); + pa_assert(length); if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) b = pa_xnew(pa_memblock, 1); @@ -374,7 +369,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free pa_assert(p); pa_assert(d); - pa_assert(length > 0); + pa_assert(length); pa_assert(length != (size_t) -1); pa_assert(free_cb); @@ -423,7 +418,6 @@ void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) { /* No lock necessary */ pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) { int r; - pa_assert(b); pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0); @@ -588,7 +582,7 @@ static void memblock_make_local(pa_memblock *b) { pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]); - if (b->length <= b->pool->block_size - PA_ALIGN(sizeof(struct mempool_slot))) { + if (b->length <= b->pool->block_size) { struct mempool_slot *slot; if ((slot = mempool_allocate_slot(b->pool))) { @@ -676,7 +670,7 @@ static void memblock_replace_import(pa_memblock *b) { pa_mutex_unlock(seg->import->mutex); } -pa_mempool* pa_mempool_new(int shared) { +pa_mempool* pa_mempool_new(pa_bool_t shared) { pa_mempool *p; p = pa_xnew(pa_mempool, 1); @@ -690,8 +684,6 @@ pa_mempool* pa_mempool_new(int shared) { p->n_blocks = PA_MEMPOOL_SLOTS_MAX; - pa_assert(p->block_size > PA_ALIGN(sizeof(struct mempool_slot))); - if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) { pa_xfree(p); return NULL; @@ -747,7 +739,7 @@ const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) { size_t pa_mempool_block_size_max(pa_mempool *p) { pa_assert(p); - return p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock)); + return p->block_size - PA_ALIGN(sizeof(pa_memblock)); } /* No lock necessary */ @@ -764,9 +756,7 @@ void pa_mempool_vacuum(pa_mempool *p) { ; while ((slot = pa_flist_pop(list))) { - pa_shm_punch(&p->memory, - (uint8_t*) slot - (uint8_t*) p->memory.ptr + PA_ALIGN(sizeof(struct mempool_slot)), - p->block_size - PA_ALIGN(sizeof(struct mempool_slot))); + pa_shm_punch(&p->memory, (uint8_t*) slot - (uint8_t*) p->memory.ptr, p->block_size); while (pa_flist_push(p->free_slots, slot)) ; diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h index 338d1a13..8dc3f5a3 100644 --- a/src/pulsecore/memblock.h +++ b/src/pulsecore/memblock.h @@ -119,7 +119,7 @@ pa_mempool * pa_memblock_get_pool(pa_memblock *b); pa_memblock *pa_memblock_will_need(pa_memblock *b); /* The memory block manager */ -pa_mempool* pa_mempool_new(int shared); +pa_mempool* pa_mempool_new(pa_bool_t shared); void pa_mempool_free(pa_mempool *p); const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p); void pa_mempool_vacuum(pa_mempool *p); -- cgit From cfc48422c8554403155c3f5ee3476c90e9ae7b8a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 20:40:20 +0000 Subject: export a few more properties for RTP streams git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2432 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/rtp/module-rtp-send.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c index a4417538..3a526c14 100644 --- a/src/modules/rtp/module-rtp-send.c +++ b/src/modules/rtp/module-rtp-send.c @@ -289,6 +289,9 @@ int pa__init(pa_module*m) { pa_source_output_new_data_init(&data); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, "RTP Monitor Stream"); + pa_proplist_sets(data.proplist, "rtp.destination", dest); + pa_proplist_setf(data.proplist, "rtp.mtu", "%lu", (unsigned long) mtu); + pa_proplist_setf(data.proplist, "rtp.port", "%lu", (unsigned long) port); data.driver = __FILE__; data.module = m; data.source = s; -- cgit From 1a2e5a8a8026a40ab351901216ffe8dcb173cfdb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 20:45:32 +0000 Subject: add adaptive resampler to the RTP receiver, other modernizations git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2433 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/rtp/module-rtp-recv.c | 151 ++++++++++++++++++++++++++++++++------ 1 file changed, 129 insertions(+), 22 deletions(-) diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c index 92b43dd9..cff5cf8b 100644 --- a/src/modules/rtp/module-rtp-recv.c +++ b/src/modules/rtp/module-rtp-recv.c @@ -51,6 +51,7 @@ #include #include #include +#include #include "module-rtp-recv-symdef.h" @@ -69,9 +70,11 @@ PA_MODULE_USAGE( #define SAP_PORT 9875 #define DEFAULT_SAP_ADDRESS "224.0.0.56" -#define MEMBLOCKQ_MAXLENGTH (1024*170) +#define MEMBLOCKQ_MAXLENGTH (1024*1024*40) #define MAX_SESSIONS 16 #define DEATH_TIMEOUT 20 +#define RATE_UPDATE_INTERVAL (5*PA_USEC_PER_SEC) +#define LATENCY_USEC (500*PA_USEC_PER_MSEC) static const char* const valid_modargs[] = { "sink", @@ -97,6 +100,12 @@ struct session { pa_rtpoll_item *rtpoll_item; pa_atomic_t timestamp; + + pa_smoother *smoother; + pa_usec_t intended_latency; + pa_usec_t sink_latency; + + pa_usec_t last_rate_update; }; struct userdata { @@ -133,7 +142,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t } /* Called from I/O thread context */ -static int sink_input_pop(pa_sink_input *i, size_t length, pa_memchunk *chunk) { +static int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) { struct session *s; pa_sink_input_assert_ref(i); pa_assert_se(s = i->userdata); @@ -146,6 +155,26 @@ static int sink_input_pop(pa_sink_input *i, size_t length, pa_memchunk *chunk) { return 0; } +/* Called from I/O thread context */ +static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct session *s; + + pa_sink_input_assert_ref(i); + pa_assert_se(s = i->userdata); + + pa_memblockq_rewind(s->memblockq, nbytes); +} + +/* Called from thread context */ +static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { + struct session *s; + + pa_sink_input_assert_ref(i); + pa_assert_se(s = i->userdata); + + pa_memblockq_set_maxrewind(s->memblockq, nbytes); +} + /* Called from main context */ static void sink_input_kill(pa_sink_input* i) { struct session *s; @@ -211,20 +240,82 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) { pa_memblockq_seek(s->memblockq, delta * s->rtp_context.frame_size, PA_SEEK_RELATIVE); + pa_rtclock_get(&now); + + pa_smoother_put(s->smoother, pa_timeval_load(&now), pa_bytes_to_usec(pa_memblockq_get_write_index(s->memblockq), &s->sink_input->sample_spec)); + if (pa_memblockq_push(s->memblockq, &chunk) < 0) { - /* queue overflow, let's flush it and try again */ - pa_memblockq_flush(s->memblockq); - pa_memblockq_push(s->memblockq, &chunk); + pa_log_warn("Queue overrun"); + pa_memblockq_seek(s->memblockq, chunk.length, PA_SEEK_RELATIVE); } - /* The next timestamp we expect */ - s->offset = s->rtp_context.timestamp + (chunk.length / s->rtp_context.frame_size); + pa_log("blocks in q: %u", pa_memblockq_get_nblocks(s->memblockq)); pa_memblock_unref(chunk.memblock); - pa_rtclock_get(&now); + /* The next timestamp we expect */ + s->offset = s->rtp_context.timestamp + (chunk.length / s->rtp_context.frame_size); + pa_atomic_store(&s->timestamp, now.tv_sec); + if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) { + pa_usec_t wi, ri, render_delay, sink_delay = 0, latency, fix; + unsigned fix_samples; + + pa_log("Updating sample rate"); + + wi = pa_smoother_get(s->smoother, pa_timeval_load(&now)); + ri = pa_bytes_to_usec(pa_memblockq_get_read_index(s->memblockq), &s->sink_input->sample_spec); + + if (PA_MSGOBJECT(s->sink_input->sink)->process_msg(PA_MSGOBJECT(s->sink_input->sink), PA_SINK_MESSAGE_GET_LATENCY, &sink_delay, 0, NULL) < 0) + sink_delay = 0; + + render_delay = pa_bytes_to_usec(pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq), &s->sink_input->sink->sample_spec); + + if (ri > render_delay+sink_delay) + ri -= render_delay+sink_delay; + else + ri = 0; + + if (wi < ri) + latency = 0; + else + latency = wi - ri; + + pa_log_debug("Write index deviates by %0.2f ms, expected %0.2f ms", (double) latency/PA_USEC_PER_MSEC, (double) s->intended_latency/PA_USEC_PER_MSEC); + + /* Calculate deviation */ + if (latency < s->intended_latency) + fix = s->intended_latency - latency; + else + fix = latency - s->intended_latency; + + /* How many samples is this per second? */ + fix_samples = fix * s->sink_input->thread_info.sample_spec.rate / RATE_UPDATE_INTERVAL; + + /* Check if deviation is in bounds */ + if (fix_samples > s->sink_input->sample_spec.rate*.20) + pa_log_debug("Hmmm, rate fix is too large (%lu Hz), not applying.", (unsigned long) fix_samples); + + /* Fix up rate */ + if (latency < s->intended_latency) + s->sink_input->sample_spec.rate -= fix_samples; + else + s->sink_input->sample_spec.rate += fix_samples; + + pa_resampler_set_input_rate(s->sink_input->thread_info.resampler, s->sink_input->sample_spec.rate); + + pa_log_debug("Updated sampling rate to %lu Hz.", (unsigned long) s->sink_input->sample_spec.rate); + + s->last_rate_update = pa_timeval_load(&now); + } + + if (pa_memblockq_is_readable(s->memblockq) && + s->sink_input->thread_info.underrun_for > 0) { + pa_log_debug("Requesting rewind due to end of underrun"); + pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE); + } + return 1; } @@ -310,7 +401,6 @@ fail: static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) { struct session *s = NULL; - char *c; pa_sink *sink; int fd = -1; pa_memchunk silence; @@ -325,33 +415,41 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in goto fail; } - if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) { + if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, TRUE))) { pa_log("Sink does not exist."); goto fail; } + pa_rtclock_get(&now); + s = pa_xnew0(struct session, 1); s->userdata = u; s->first_packet = FALSE; s->sdp_info = *sdp_info; s->rtpoll_item = NULL; - - pa_rtclock_get(&now); + s->intended_latency = LATENCY_USEC; + s->smoother = pa_smoother_new(PA_USEC_PER_SEC*5, PA_USEC_PER_SEC*2, TRUE, 10); + pa_smoother_set_time_offset(s->smoother, pa_timeval_load(&now)); + s->last_rate_update = pa_timeval_load(&now); pa_atomic_store(&s->timestamp, now.tv_sec); if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0) goto fail; - c = pa_sprintf_malloc("RTP Stream%s%s%s", - sdp_info->session_name ? " (" : "", - sdp_info->session_name ? sdp_info->session_name : "", - sdp_info->session_name ? ")" : ""); - pa_sink_input_new_data_init(&data); data.sink = sink; data.driver = __FILE__; - pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, c); - pa_xfree(c); + pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream"); + pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, + "RTP Stream%s%s%s", + sdp_info->session_name ? " (" : "", + sdp_info->session_name ? sdp_info->session_name : "", + sdp_info->session_name ? ")" : ""); + + if (sdp_info->session_name) + pa_proplist_sets(data.proplist, "rtp.session", sdp_info->session_name); + pa_proplist_sets(data.proplist, "rtp.origin", sdp_info->origin); + pa_proplist_setf(data.proplist, "rtp.payload", "%u", (unsigned) sdp_info->payload); data.module = u->module; pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec); @@ -366,19 +464,26 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in s->sink_input->userdata = s; s->sink_input->parent.process_msg = sink_input_process_msg; - s->sink_input->pop = sink_input_pop; + s->sink_input->pop = sink_input_pop_cb; + s->sink_input->process_rewind = sink_input_process_rewind_cb; + s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; s->sink_input->kill = sink_input_kill; s->sink_input->attach = sink_input_attach; s->sink_input->detach = sink_input_detach; pa_sink_input_get_silence(s->sink_input, &silence); + s->sink_latency = pa_sink_input_set_requested_latency(s->sink_input, s->intended_latency/2); + + if (s->intended_latency < s->sink_latency*2) + s->intended_latency = s->sink_latency*2; + s->memblockq = pa_memblockq_new( 0, MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, pa_frame_size(&s->sink_input->sample_spec), - pa_bytes_per_second(&s->sink_input->sample_spec)/10+1, + pa_usec_to_bytes(s->intended_latency - s->sink_latency, &s->sink_input->sample_spec), 0, 0, &silence); @@ -423,12 +528,14 @@ static void session_free(struct session *s) { pa_sdp_info_destroy(&s->sdp_info); pa_rtp_context_destroy(&s->rtp_context); + pa_smoother_free(s->smoother); + pa_xfree(s); } static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) { struct userdata *u = userdata; - int goodbye; + pa_bool_t goodbye = FALSE; pa_sdp_info info; struct session *s; -- cgit From b57c520149e12339417813cbee07442ccbc788f7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 22:14:45 +0000 Subject: add pa_vsnprintf() git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2434 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/core-util.c | 20 ++++++++++++++++++-- src/pulsecore/core-util.h | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index df110966..c8ea4f52 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -1592,12 +1592,28 @@ int pa_snprintf(char *str, size_t size, const char *format, ...) { pa_assert(format); va_start(ap, format); - ret = vsnprintf(str, size, format, ap); + ret = pa_vsnprintf(str, size, format, ap); va_end(ap); + return ret; +} + +/* Same as vsnprintf, but guarantees NUL-termination on every platform */ +int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap) { + int ret; + + pa_assert(str); + pa_assert(size > 0); + pa_assert(format); + + ret = vsnprintf(str, size, format, ap); + str[size-1] = 0; - return ret; + if (ret < 0) + ret = strlen(str); + + return PA_MIN((int) size-1, ret); } /* Truncate the specified string, but guarantee that the string diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index e9313d4b..ec4cdc43 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -128,6 +128,7 @@ int pa_atou(const char *s, uint32_t *ret_u); int pa_atof(const char *s, float *ret_f); int pa_snprintf(char *str, size_t size, const char *format, ...); +int pa_vsnprintf(char *str, size_t size, const char *format, va_list ap); char *pa_truncate_utf8(char *c, size_t l); -- cgit From 86ea73acd366817e555e5a83fb9f3db4dc88b791 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 22:21:05 +0000 Subject: reduce malloc() usage when logging, to minimize the hit of logging in RT threads. Not complete yet, i18n still uses malloc git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2435 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/log.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c index a8985783..b5929ec4 100644 --- a/src/pulsecore/log.c +++ b/src/pulsecore/log.c @@ -109,9 +109,13 @@ void pa_log_levelv_meta( va_list ap) { const char *e; - char *text, *t, *n, *location; + char *t, *n; int saved_errno = errno; + /* We don't use dynamic memory allocation here to minimize the hit + * in RT threads */ + char text[1024], location[128]; + pa_assert(level < PA_LOG_LEVEL_MAX); pa_assert(format); @@ -123,14 +127,14 @@ void pa_log_levelv_meta( return; } - text = pa_vsprintf_malloc(format, ap); + pa_vsnprintf(text, sizeof(text), format, ap); if (getenv(ENV_LOGMETA) && file && line > 0 && func) - location = pa_sprintf_malloc("[%s:%i %s()] ", file, line, func); + pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func); else if (file) - location = pa_sprintf_malloc("%s: ", pa_path_get_filename(file)); + pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file)); else - location = pa_xstrdup(""); + location[0] = 0; if (!pa_utf8_valid(text)) pa_log_level(level, __FILE__": invalid UTF-8 string following below:"); @@ -162,6 +166,8 @@ void pa_log_levelv_meta( } #endif + /* We shouldn't be using dynamic allocation here to + * minimize the hit in RT threads */ local_t = pa_utf8_to_locale(t); if (!local_t) fprintf(stderr, "%c: %s%s%s%s\n", level_to_char[level], location, prefix, t, suffix); @@ -193,11 +199,10 @@ void pa_log_levelv_meta( #endif case PA_LOG_USER: { - char *x; + char x[1024]; - x = pa_sprintf_malloc("%s%s", location, t); + pa_snprintf(x, sizeof(x), "%s%s", location, t); user_log_func(level, x); - pa_xfree(x); break; } @@ -208,9 +213,6 @@ void pa_log_levelv_meta( } } - pa_xfree(text); - pa_xfree(location); - errno = saved_errno; } -- cgit From e0dc1e483c2be1f48c1e357505b6eecf606d7348 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 22:22:41 +0000 Subject: Print message when stream started playback, use terminal sequence to clear line when printing that message git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2436 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/utils/pacat.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/utils/pacat.c b/src/utils/pacat.c index e802015b..fc9d56d6 100644 --- a/src/utils/pacat.c +++ b/src/utils/pacat.c @@ -44,6 +44,8 @@ #error Invalid PulseAudio API version #endif +#define CLEAR_LINE "\x1B[K" + static enum { RECORD, PLAYBACK } mode = PLAYBACK; static pa_context *context = NULL; @@ -206,29 +208,38 @@ static void stream_suspended_callback(pa_stream *s, void *userdata) { if (verbose) { if (pa_stream_is_suspended(s)) - fprintf(stderr, "Stream device suspended.\n"); + fprintf(stderr, "Stream device suspended." CLEAR_LINE " \n"); else - fprintf(stderr, "Stream device resumed.\n"); + fprintf(stderr, "Stream device resumed." CLEAR_LINE " \n"); } } static void stream_underflow_callback(pa_stream *s, void *userdata) { assert(s); - fprintf(stderr, "Underrun.\n"); + if (verbose) + fprintf(stderr, "Stream underrun." CLEAR_LINE " \n"); } static void stream_overflow_callback(pa_stream *s, void *userdata) { assert(s); - fprintf(stderr, "Overrun.\n"); + if (verbose) + fprintf(stderr, "Stream overrun." CLEAR_LINE " \n"); +} + +static void stream_started_callback(pa_stream *s, void *userdata) { + assert(s); + + if (verbose) + fprintf(stderr, "Stream started." CLEAR_LINE " \n"); } static void stream_moved_callback(pa_stream *s, void *userdata) { assert(s); if (verbose) - fprintf(stderr, "Stream moved to device %s (%u, %ssuspended).\n", pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : "not "); + fprintf(stderr, "Stream moved to device %s (%u, %ssuspended)." CLEAR_LINE " \n", pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : "not "); } /* This is called whenever the context status changes */ @@ -249,7 +260,7 @@ static void context_state_callback(pa_context *c, void *userdata) { assert(!stream); if (verbose) - fprintf(stderr, "Connection established.\n"); + fprintf(stderr, "Connection established." CLEAR_LINE " \n"); if (!(stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL))) { fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c))); @@ -263,6 +274,7 @@ static void context_state_callback(pa_context *c, void *userdata) { pa_stream_set_moved_callback(stream, stream_moved_callback, NULL); pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL); pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL); + pa_stream_set_started_callback(stream, stream_started_callback, NULL); if (latency > 0) { memset(&buffer_attr, 0, sizeof(buffer_attr)); -- cgit From f021538d918a86c47010f663e911c77657af5a07 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 22:23:14 +0000 Subject: export a couple of more functions from libpulse git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2437 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/map-file | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/map-file b/src/map-file index ffa5d103..d9189743 100644 --- a/src/map-file +++ b/src/map-file @@ -30,6 +30,7 @@ pa_context_get_autoload_info_by_name; pa_context_get_autoload_info_list; pa_context_get_client_info; pa_context_get_client_info_list; +pa_context_get_index; pa_context_get_module_info; pa_context_get_module_info_list; pa_context_get_protocol_version; @@ -61,7 +62,11 @@ pa_context_move_sink_input_by_name; pa_context_move_source_output_by_index; pa_context_move_source_output_by_name; pa_context_new; +pa_context_new_with_proplist; pa_context_play_sample; +pa_context_play_sample_with_proplist; +pa_context_proplist_remove; +pa_context_proplist_update; pa_context_ref; pa_context_remove_autoload_by_index; pa_context_remove_autoload_by_name; @@ -128,14 +133,19 @@ pa_operation_unref; pa_parse_sample_format; pa_path_get_filename; pa_proplist_free; +pa_proplist_contains; +pa_proplist_clear; +pa_proplist_copy; pa_proplist_get; pa_proplist_gets; pa_proplist_iterate; -pa_proplist_merge; +pa_proplist_update; pa_proplist_new; -pa_proplist_put; -pa_proplist_puts; -pa_proplist_remove; +pa_proplist_set; +pa_proplist_sets; +pa_proplist_setf; +pa_proplist_unset; +pa_proplist_unset_many; pa_proplist_to_string; pa_sample_format_to_string; pa_sample_size; @@ -174,10 +184,14 @@ pa_stream_get_sample_spec; pa_stream_get_state; pa_stream_get_time; pa_stream_get_timing_info; +pa_stream_is_corked; pa_stream_is_suspended; pa_stream_new; +pa_stream_new_with_proplist; pa_stream_peek; pa_stream_prebuf; +pa_stream_proplist_remove; +pa_stream_proplist_update; pa_stream_readable_size; pa_stream_ref; pa_stream_set_buffer_attr; @@ -186,6 +200,7 @@ pa_stream_set_moved_callback; pa_stream_set_name; pa_stream_set_overflow_callback; pa_stream_set_read_callback; +pa_stream_set_started_callback; pa_stream_set_state_callback; pa_stream_set_suspended_callback; pa_stream_set_underflow_callback; @@ -221,6 +236,7 @@ pa_timeval_cmp; pa_timeval_diff; pa_timeval_load; pa_timeval_store; +pa_timeval_sub; pa_usec_to_bytes; pa_utf8_filter; pa_utf8_to_locale; -- cgit From 74f8a67e98965127416c8f8b720b26cfed927596 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 22:24:18 +0000 Subject: fix suspend for alsa sink git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2438 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index 9c4da07a..f4211ed3 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -868,6 +868,9 @@ static void sink_update_requested_latency_cb(pa_sink *s) { snd_pcm_sframes_t before; pa_assert(u); + if (!u->pcm_handle) + return; + before = u->hwbuf_unused_frames; update_sw_params(u); @@ -1003,7 +1006,7 @@ static void thread_func(void *userdata) { * we have filled the buffer at least once * completely.*/ - pa_log_debug("Cutting sleep time for the initial iterations by half."); + /*pa_log_debug("Cutting sleep time for the initial iterations by half.");*/ sleep_usec /= 2; } -- cgit From 99a451655a2db4c6c7a70f7e54b68991a046ff16 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 23:16:28 +0000 Subject: don't access stream before it is valid git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2439 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulse/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pulse/stream.c b/src/pulse/stream.c index bd633cf0..4268fd6f 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -370,7 +370,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, PA_GCC_UNUSED u if (c->version >= 13) { - if (s->direction == PA_STREAM_RECORD) { + if (command == PA_COMMAND_RECORD_STREAM_MOVED) { if (pa_tagstruct_getu32(t, &maxlength) < 0 || pa_tagstruct_getu32(t, &fragsize) < 0 || pa_tagstruct_get_usec(t, &usec) < 0) { -- cgit From e3c5a777504f0c19027806c1639c596469259324 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 23:16:49 +0000 Subject: fix moving of record streams git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2440 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/source-output.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 3940d768..5c36937a 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -635,13 +635,25 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) { if (o->thread_info.resampler) pa_resampler_free(o->thread_info.resampler); o->thread_info.resampler = new_resampler; - } - pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); + pa_memblockq_free(o->thread_info.delay_memblockq); + + o->thread_info.delay_memblockq = pa_memblockq_new( + 0, + MEMBLOCKQ_MAXLENGTH, + 0, + pa_frame_size(&o->source->sample_spec), + 0, + 1, + 0, + &o->source->silence); + } pa_source_update_status(origin); pa_source_update_status(dest); + pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL); + if (o->moved) o->moved(o); -- cgit From 813d40c13d27b15e647e014e0fb1e4508a2532a0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 23:17:22 +0000 Subject: fix up requested latency when we move a record stream git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2441 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/pulsecore/source.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 426906eb..c767abcf 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -577,8 +577,6 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o)); - pa_source_output_update_max_rewind(o, s->thread_info.max_rewind); - pa_assert(!o->thread_info.attached); o->thread_info.attached = TRUE; @@ -587,7 +585,12 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_ pa_source_output_set_state_within_thread(o, o->state); - pa_source_invalidate_requested_latency(s); + pa_source_output_update_max_rewind(o, s->thread_info.max_rewind); + + /* We don't just invalidate the requested latency here, + * because if we are in a move we might need to fix up the + * requested latency. */ + pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency); return 0; } -- cgit From 734f071df7a0a3678cafb4db62e0a7ba26585e93 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 23:17:58 +0000 Subject: decrease default tsched buffer to 2s to reduce overall memory consumption git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2442 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-sink.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c index f4211ed3..95a72fdc 100644 --- a/src/modules/module-alsa-sink.c +++ b/src/modules/module-alsa-sink.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio is free software; you can redistribute it and/or modify @@ -92,10 +92,10 @@ static const char* const valid_modargs[] = { }; #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (5*PA_USEC_PER_SEC) /* 5s */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ #define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ -#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ -#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ struct userdata { pa_core *core; -- cgit From 43dfc2a00035af3eac5bef812877d7b3fd61efdc Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 23:19:37 +0000 Subject: follow recent alsa sink changes in the alsa source git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2443 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/modules/module-alsa-source.c | 299 +++++++++++++++++++++------------------ 1 file changed, 158 insertions(+), 141 deletions(-) diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c index 4838ad27..e3090109 100644 --- a/src/modules/module-alsa-source.c +++ b/src/modules/module-alsa-source.c @@ -3,7 +3,7 @@ /*** This file is part of PulseAudio. - Copyright 2004-2006 Lennart Poettering + Copyright 2004-2008 Lennart Poettering Copyright 2006 Pierre Ossman for Cendio AB PulseAudio is free software; you can redistribute it and/or modify @@ -93,10 +93,10 @@ static const char* const valid_modargs[] = { }; #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (5*PA_USEC_PER_SEC) -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) -#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ -#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ +#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ +#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC) /* 3ms */ struct userdata { pa_core *core; @@ -160,6 +160,31 @@ static void fix_tsched_watermark(struct userdata *u) { u->tsched_watermark = min_wakeup; } +static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) { + pa_usec_t wm, usec; + + pa_assert(u); + + usec = pa_source_get_requested_latency_within_thread(u->source); + + if (usec == (pa_usec_t) -1) + usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); + +/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ + + wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec); + + if (usec >= wm) { + *sleep_usec = usec - wm; + *process_usec = wm; + } else + *process_usec = *sleep_usec = usec /= 2; + +/* pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */ + + return usec; +} + static int try_recover(struct userdata *u, const char *call, int err) { pa_assert(u); pa_assert(call); @@ -167,10 +192,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { pa_log_debug("%s: %s", call, snd_strerror(err)); - if (err == -EAGAIN) { - pa_log_debug("%s: EAGAIN", call); - return 1; - } + pa_assert(err != -EAGAIN); if (err == -EPIPE) pa_log_debug("%s: Buffer overrun!", call); @@ -184,7 +206,7 @@ static int try_recover(struct userdata *u, const char *call, int err) { return -1; } -static void check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { +static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { size_t left_to_record; if (n*u->frame_size < u->hwbuf_size) @@ -192,9 +214,9 @@ static void check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { else left_to_record = 0; - if (left_to_record > 0) - pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); - else { + if (left_to_record > 0) { +/* pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */ + } else { pa_log_info("Overrun!"); if (u->use_tsched) { @@ -208,111 +230,121 @@ static void check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) { (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); } } + + return left_to_record; } -static int mmap_read(struct userdata *u) { +static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_bool_t checked_left_to_record = FALSE; + pa_usec_t max_sleep_usec, process_usec; + size_t left_to_record; pa_assert(u); pa_source_assert_ref(u->source); + if (u->use_tsched) + hw_sleep_time(u, &max_sleep_usec, &process_usec); + for (;;) { snd_pcm_sframes_t n; - int err, r; - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t offset, frames; - pa_memchunk chunk; - void *p; + int r; snd_pcm_hwsync(u->pcm_handle); if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { - if ((r = try_recover(u, "snd_pcm_avail_update", err)) == 0) + if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) continue; - else if (r > 0) - return work_done; return r; } - if (checked_left_to_record) { - check_left_to_record(u, n); - checked_left_to_record = TRUE; - } + left_to_record = check_left_to_record(u, n); + + if (u->use_tsched) + if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > max_sleep_usec/2) + break; if (PA_UNLIKELY(n <= 0)) - return work_done; + break; - frames = n; + for (;;) { + int err; + const snd_pcm_channel_area_t *areas; + snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n; + pa_memchunk chunk; + void *p; - pa_log_debug("%lu frames to read", (unsigned long) frames); +/* pa_log_debug("%lu frames to read", (unsigned long) frames); */ - if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { + if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { - if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) - continue; - else if (r > 0) - return work_done; + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) + continue; - return r; - } + return r; + } - /* Make sure that if these memblocks need to be copied they will fit into one slot */ - if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size) - frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size; + /* Make sure that if these memblocks need to be copied they will fit into one slot */ + if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size) + frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size; - /* Check these are multiples of 8 bit */ - pa_assert((areas[0].first & 7) == 0); - pa_assert((areas[0].step & 7)== 0); + /* Check these are multiples of 8 bit */ + pa_assert((areas[0].first & 7) == 0); + pa_assert((areas[0].step & 7)== 0); - /* We assume a single interleaved memory buffer */ - pa_assert((areas[0].first >> 3) == 0); - pa_assert((areas[0].step >> 3) == u->frame_size); + /* We assume a single interleaved memory buffer */ + pa_assert((areas[0].first >> 3) == 0); + pa_assert((areas[0].step >> 3) == u->frame_size); - p = (uint8_t*) areas[0].addr + (offset * u->frame_size); + p = (uint8_t*) areas[0].addr + (offset * u->frame_size); - chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE); - chunk.length = pa_memblock_get_length(chunk.memblock); - chunk.index = 0; + chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE); + chunk.length = pa_memblock_get_length(chunk.memblock); + chunk.index = 0; - pa_source_post(u->source, &chunk); - pa_memblock_unref_fixed(chunk.memblock); + pa_source_post(u->source, &chunk); + pa_memblock_unref_fixed(chunk.memblock); - if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { + if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { - if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) - continue; - else if (r > 0) - return work_done; + if ((r = try_recover(u, "snd_pcm_mmap_commit", err)) == 0) + continue; - return r; - } + return r; + } - work_done = 1; + work_done = 1; - u->frame_index += frames; + u->frame_index += frames; - pa_log_debug("read %lu frames", (unsigned long) frames); +/* pa_log_debug("read %lu frames", (unsigned long) frames); */ - if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) - return work_done; + if (frames >= (snd_pcm_uframes_t) n) + break; + + n -= frames; + } } + + *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec; + return work_done; } -static int unix_read(struct userdata *u) { +static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) { int work_done = 0; - pa_bool_t checked_left_to_record = FALSE; + pa_usec_t max_sleep_usec, process_usec; + size_t left_to_record; pa_assert(u); pa_source_assert_ref(u->source); + if (u->use_tsched) + hw_sleep_time(u, &max_sleep_usec, &process_usec); + for (;;) { - void *p; - snd_pcm_sframes_t n, frames; + snd_pcm_sframes_t n; int r; - pa_memchunk chunk; snd_pcm_hwsync(u->pcm_handle); @@ -320,61 +352,69 @@ static int unix_read(struct userdata *u) { if ((r = try_recover(u, "snd_pcm_avail_update", n)) == 0) continue; - else if (r > 0) - return work_done; return r; } - if (checked_left_to_record) { - check_left_to_record(u, n); - checked_left_to_record = TRUE; - } + left_to_record = check_left_to_record(u, n); + + if (u->use_tsched) + if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > max_sleep_usec/2) + break; if (PA_UNLIKELY(n <= 0)) return work_done; - chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); + for (;;) { + void *p; + snd_pcm_sframes_t frames; + pa_memchunk chunk; - frames = pa_memblock_get_length(chunk.memblock) / u->frame_size; + chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); - if (frames > n) - frames = n; + frames = pa_memblock_get_length(chunk.memblock) / u->frame_size; - pa_log_debug("%lu frames to read", (unsigned long) n); + if (frames > n) + frames = n; - p = pa_memblock_acquire(chunk.memblock); - frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames); - pa_memblock_release(chunk.memblock); +/* pa_log_debug("%lu frames to read", (unsigned long) n); */ - pa_assert(frames != 0); + p = pa_memblock_acquire(chunk.memblock); + frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, frames); + pa_memblock_release(chunk.memblock); - if (PA_UNLIKELY(frames < 0)) { - pa_memblock_unref(chunk.memblock); + pa_assert(frames != 0); - if ((r = try_recover(u, "snd_pcm_readi", n)) == 0) - continue; - else if (r > 0) - return work_done; + if (PA_UNLIKELY(frames < 0)) { + pa_memblock_unref(chunk.memblock); - return r; - } + if ((r = try_recover(u, "snd_pcm_readi", n)) == 0) + continue; - chunk.index = 0; - chunk.length = frames * u->frame_size; + return r; + } - pa_source_post(u->source, &chunk); - pa_memblock_unref(chunk.memblock); + chunk.index = 0; + chunk.length = frames * u->frame_size; - work_done = 1; + pa_source_post(u->source, &chunk); + pa_memblock_unref(chunk.memblock); - u->frame_index += frames; + work_done = 1; - pa_log_debug("read %lu frames", (unsigned long) frames); + u->frame_index += frames; - if (PA_LIKELY(frames >= n)) - return work_done; +/* pa_log_debug("read %lu frames", (unsigned long) frames); */ + + if (frames >= n) + break; + + n -= frames; + } } + + *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec; + return work_done; } static void update_smoother(struct userdata *u) { @@ -455,30 +495,6 @@ static int suspend(struct userdata *u) { return 0; } -static pa_usec_t hw_sleep_time(struct userdata *u) { - pa_usec_t wm, usec; - - pa_assert(u); - - usec = pa_source_get_requested_latency_within_thread(u->source); - - if (usec == (pa_usec_t) -1) - usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec); - -/* pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ - - wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec); - - if (usec >= wm) - usec -= wm; - else - usec /= 2; - -/* pa_log_debug("after watermark: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */ - - return usec; -} - static int update_sw_params(struct userdata *u) { snd_pcm_uframes_t avail_min; int err; @@ -516,10 +532,10 @@ static int update_sw_params(struct userdata *u) { avail_min = 1; if (u->use_tsched) { - pa_usec_t usec; + pa_usec_t sleep_usec, process_usec; - usec = hw_sleep_time(u); - avail_min += pa_usec_to_bytes(usec, &u->source->sample_spec); + hw_sleep_time(u, &sleep_usec, &process_usec); + avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec); } pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min); @@ -794,9 +810,11 @@ static int source_set_mute_cb(pa_source *s) { static void source_update_requested_latency_cb(pa_source *s) { struct userdata *u = s->userdata; - pa_assert(u); + if (!u->pcm_handle) + return; + update_sw_params(u); } @@ -816,43 +834,42 @@ static void thread_func(void *userdata) { for (;;) { int ret; - pa_log_debug("loop"); +/* pa_log_debug("loop"); */ /* Read some data and pass it to the sources */ if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { int work_done = 0; + pa_usec_t sleep_usec; if (u->use_mmap) - work_done = mmap_read(u); + work_done = mmap_read(u, &sleep_usec); else - work_done = unix_read(u); + work_done = unix_read(u, &sleep_usec); if (work_done < 0) goto fail; - pa_log_debug("work_done = %i", work_done); +/* pa_log_debug("work_done = %i", work_done); */ if (work_done) update_smoother(u); if (u->use_tsched) { - pa_usec_t usec, cusec; + pa_usec_t cusec; /* OK, the capture buffer is now empty, let's * calculate when to wake up next */ - usec = hw_sleep_time(u); - - pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) usec / PA_USEC_PER_MSEC); +/* pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */ /* Convert from the sound card time domain to the * system time domain */ - cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), usec); + cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec); - pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); +/* pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */ /* We don't trust the conversion, so we wake up whatever comes first */ - pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); + pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec)); } } else if (u->use_tsched) @@ -887,7 +904,7 @@ static void thread_func(void *userdata) { snd_pcm_start(u->pcm_handle); } - if (revents) + if (revents && u->use_tsched) pa_log_debug("Wakeup from ALSA! (%i)", revents); } } -- cgit From 3aadad12a08b8d976dabfeb5be3774be2e32a981 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 15 May 2008 23:25:11 +0000 Subject: update protocol spec git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2444 fefdeb5f-60dc-0310-8127-8f9354f1896f --- PROTOCOL | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/PROTOCOL b/PROTOCOL index 74c08b49..4439c713 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -84,6 +84,7 @@ New opcodes for notifications: New fields for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM request at the end: peak_detect (bool) + adjust_latency (bool) Replace field "name" for PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM at the end: @@ -124,3 +125,12 @@ New field for PA_COMMAND_CREATE_PLAYBACK_STREAM at the end: Buffer attributes for PA_COMMAND_CREATE_PLAYBACK_STREAM and PA_COMMAND_CREATE_RECORD_STREAM may now be 0 for default values. + +New filed for PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR, +PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end: + + adjust_latency (bool) + +new message: + + PA_COMMAND_STARTED -- cgit