diff options
Diffstat (limited to 'src/pulse')
-rw-r--r-- | src/pulse/context.c | 83 | ||||
-rw-r--r-- | src/pulse/context.h | 7 | ||||
-rw-r--r-- | src/pulse/def.h | 7 | ||||
-rw-r--r-- | src/pulse/internal.h | 8 | ||||
-rw-r--r-- | src/pulse/stream.c | 92 | ||||
-rw-r--r-- | src/pulse/stream.h | 9 |
6 files changed, 196 insertions, 10 deletions
diff --git a/src/pulse/context.c b/src/pulse/context.c index 00dffc25..9fb9e726 100644 --- a/src/pulse/context.c +++ b/src/pulse/context.c @@ -99,9 +99,12 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_EXTENSION] = pa_command_extension, [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event, [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event, - [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event + [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event, + [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr, + [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr }; static void context_free(pa_context *c); +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata); pa_context *pa_context_new(pa_mainloop_api *mainloop, const char *name) { return pa_context_new_with_proplist(mainloop, name, NULL); @@ -141,6 +144,8 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char * if (name) pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name); + c->no_fail = FALSE; + c->system_bus = c->session_bus = NULL; c->mainloop = mainloop; c->client = NULL; c->pstream = NULL; @@ -235,6 +240,16 @@ static void context_free(pa_context *c) { context_unlink(c); + if (c->system_bus) { + dbus_connection_remove_filter(pa_dbus_wrap_connection_get(c->system_bus), filter_cb, c); + pa_dbus_wrap_connection_free(c->system_bus); + } + + if (c->session_bus) { + dbus_connection_remove_filter(pa_dbus_wrap_connection_get(c->session_bus), filter_cb, c); + pa_dbus_wrap_connection_free(c->session_bus); + } + if (c->record_streams) pa_dynarray_free(c->record_streams, NULL, NULL); if (c->playback_streams) @@ -726,6 +741,32 @@ fail: static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userdata); +static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wrap_connection **conn) { + DBusError error; + + pa_assert(c); + pa_assert(conn); + + dbus_error_init(&error); + if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, type, &error)) || dbus_error_is_set(&error)) { + pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message); + goto finish; + } + + if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) { + pa_log_warn("Failed to add filter function"); + goto finish; + } + + if (pa_dbus_add_matches( + pa_dbus_wrap_connection_get(*conn), &error, + "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio',arg1=''", NULL) < 0) + pa_log_warn("Unable to track org.pulseaudio: %s: %s", error.name, error.message); + + finish: + dbus_error_free(&error); +} + static int try_next_connection(pa_context *c) { char *u = NULL; int r = -1; @@ -758,7 +799,14 @@ static int try_next_connection(pa_context *c) { } #endif - pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); + if (c->no_fail) { + if (!c->system_bus) + track_pulseaudio_on_dbus(c, DBUS_BUS_SYSTEM, &c->system_bus); + if (!c->session_bus) + track_pulseaudio_on_dbus(c, DBUS_BUS_SESSION, &c->session_bus); + } else + pa_context_fail(c, PA_ERR_CONNECTIONREFUSED); + goto finish; } @@ -815,6 +863,34 @@ finish: pa_context_unref(c); } +static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) { + pa_context *c = userdata; + pa_bool_t is_session; + + pa_assert(bus); + pa_assert(message); + pa_assert(c); + + if (c->state != PA_CONTEXT_CONNECTING) + goto finish; + + is_session = bus == pa_dbus_wrap_connection_get(c->session_bus); + pa_log_debug("Rock!! PulseAudio is baack on %s bus", is_session ? "session" : "system"); + + if (is_session) { + /* The user instance via PF_LOCAL */ + c->server_list = prepend_per_user(c->server_list); + } else { + /* The system wide instance via PF_LOCAL */ + c->server_list = pa_strlist_prepend(c->server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET); + } + + try_next_connection(c); + +finish: + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + int pa_context_connect( pa_context *c, const char *server, @@ -828,7 +904,7 @@ int pa_context_connect( PA_CHECK_VALIDITY(c, !pa_detect_fork(), PA_ERR_FORKED); PA_CHECK_VALIDITY(c, c->state == PA_CONTEXT_UNCONNECTED, PA_ERR_BADSTATE); - PA_CHECK_VALIDITY(c, !(flags & ~PA_CONTEXT_NOAUTOSPAWN), PA_ERR_INVALID); + PA_CHECK_VALIDITY(c, !(flags & ~(PA_CONTEXT_NOAUTOSPAWN|PA_CONTEXT_NOFAIL)), PA_ERR_INVALID); PA_CHECK_VALIDITY(c, !server || *server, PA_ERR_INVALID); if (!server) @@ -836,6 +912,7 @@ int pa_context_connect( pa_context_ref(c); + c->no_fail = flags & PA_CONTEXT_NOFAIL; pa_assert(!c->server_list); if (server) { diff --git a/src/pulse/context.h b/src/pulse/context.h index c32cf443..139d0e0b 100644 --- a/src/pulse/context.h +++ b/src/pulse/context.h @@ -207,9 +207,10 @@ pa_context_state_t pa_context_get_state(pa_context *c); connect to the default server. This routine may but will not always return synchronously on error. Use pa_context_set_state_callback() to be notified when the connection is established. If flags doesn't have -PA_NOAUTOSPAWN set and no specific server is specified or accessible a -new daemon is spawned. If api is non-NULL, the functions specified in -the structure are used when forking a new child process. */ +PA_CONTEXT_NOAUTOSPAWN set and no specific server is specified or +accessible a new daemon is spawned. If api is non-NULL, the functions +specified in the structure are used when forking a new child +process. */ int pa_context_connect(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api); /** Terminate the context connection immediately */ diff --git a/src/pulse/def.h b/src/pulse/def.h index 9418e96f..cae08942 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -109,13 +109,16 @@ typedef enum pa_operation_state { /** Some special flags for contexts. */ typedef enum pa_context_flags { - PA_CONTEXT_NOAUTOSPAWN = 1 + PA_CONTEXT_NOAUTOSPAWN = 0x0001U, /**< Disabled autospawning of the PulseAudio daemon if required */ + PA_CONTEXT_NOFAIL = 0x0002U + /**< Don't fail if the daemon is not available when pa_context_connect() is called, instead enter PA_CONTEXT_CONNECTING state and wait for the daemon to appear. \since 0.9.15 */ } pa_context_flags_t; /** \cond fulldocs */ /* Allow clients to check with #ifdef for those flags */ #define PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN +#define PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL /** \endcond */ /** The direction of a pa_stream object */ @@ -624,7 +627,7 @@ typedef struct pa_timing_info { /**< The configured latency for the sink. \since 0.9.11 */ pa_usec_t configured_source_usec; - /**< The configured latency for * the source. \since 0.9.11 */ + /**< The configured latency for the source. \since 0.9.11 */ int64_t since_underrun; /**< Bytes that were handed to the sink since the last underrun diff --git a/src/pulse/internal.h b/src/pulse/internal.h index 9646d8a6..8c242949 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -42,6 +42,7 @@ #include <pulsecore/hashmap.h> #include <pulsecore/refcnt.h> #include <pulsecore/time-smoother.h> +#include <pulsecore/dbus-util.h> #include "client-conf.h" @@ -50,6 +51,10 @@ struct pa_context { PA_REFCNT_DECLARE; + pa_bool_t no_fail:1; + pa_dbus_wrap_connection *system_bus; + pa_dbus_wrap_connection *session_bus; + pa_proplist *proplist; pa_mainloop_api* mainloop; @@ -185,6 +190,8 @@ struct pa_stream { void *started_userdata; pa_stream_event_cb_t event_callback; void *event_userdata; + pa_stream_notify_cb_t buffer_attr_callback; + void *buffer_attr_userdata; }; typedef void (*pa_operation_cb_t)(void); @@ -213,6 +220,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, p void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); +void pa_command_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata); pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata); void pa_operation_done(pa_operation *o); diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 48e3b08e..bb53b19d 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -72,6 +72,8 @@ static void reset_callbacks(pa_stream *s) { s->started_userdata = NULL; s->event_callback = NULL; s->event_userdata = NULL; + s->buffer_attr_callback = NULL; + s->buffer_attr_userdata = NULL; } pa_stream *pa_stream_new_with_proplist( @@ -396,7 +398,7 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, p const char *dn; pa_bool_t suspended; uint32_t di; - pa_usec_t usec; + pa_usec_t usec = 0; uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0; pa_assert(pd); @@ -486,6 +488,80 @@ finish: pa_context_unref(c); } +void pa_command_stream_buffer_attr(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_usec_t usec = 0; + uint32_t maxlength = 0, fragsize = 0, minreq = 0, tlength = 0, prebuf = 0; + + pa_assert(pd); + pa_assert(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED || command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED); + pa_assert(t); + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + pa_context_ref(c); + + if (c->version < 15) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (pa_tagstruct_getu32(t, &channel) < 0) { + pa_context_fail(c, PA_ERR_PROTOCOL); + goto finish; + } + + 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) { + 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(c, PA_ERR_PROTOCOL); + goto finish; + } + + if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED ? c->playback_streams : c->record_streams, channel))) + goto finish; + + if (s->state != PA_STREAM_READY) + goto finish; + + if (s->direction == PA_STREAM_RECORD) + 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; + + request_auto_timing_update(s, TRUE); + + if (s->buffer_attr_callback) + s->buffer_attr_callback(s, s->buffer_attr_userdata); + +finish: + pa_context_unref(c); +} + void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_context *c = userdata; pa_stream *s; @@ -1798,6 +1874,20 @@ void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *u s->event_userdata = userdata; } +void pa_stream_set_buffer_attr_callback(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata) { + pa_assert(s); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + if (pa_detect_fork()) + return; + + if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED) + return; + + s->buffer_attr_callback = cb; + s->buffer_attr_userdata = userdata; +} + void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { pa_operation *o = userdata; int success = 1; diff --git a/src/pulse/stream.h b/src/pulse/stream.h index e80bc65d..8e99a753 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -512,6 +512,13 @@ void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, vo * control event is received.\since 0.9.15 */ void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata); +/** Set the callback function that is called whenver the buffer + * attributes on the server side change. Please note that the buffer + * attributes can change when moving a stream to a different + * sink/source too, hence if you use this callback you should use + * pa_stream_set_moved_callback() as well. \since 0.9.15 */ +void pa_stream_set_buffer_attr_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. */ pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata); @@ -530,7 +537,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us * temporarily. Available for playback streams only. */ pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata); -/** Rename the stream.*/ +/** Rename the stream. */ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata); /** Return the current playback/recording time. This is based on the |