diff options
Diffstat (limited to 'src/modules/jack')
| -rw-r--r-- | src/modules/jack/module-jack-sink.c | 36 | ||||
| -rw-r--r-- | src/modules/jack/module-jack-source.c | 28 | ||||
| -rw-r--r-- | src/modules/jack/module-jackdbus-detect.c | 298 |
3 files changed, 331 insertions, 31 deletions
diff --git a/src/modules/jack/module-jack-sink.c b/src/modules/jack/module-jack-sink.c index 290038e7..35b0385d 100644 --- a/src/modules/jack/module-jack-sink.c +++ b/src/modules/jack/module-jack-sink.c @@ -24,19 +24,15 @@ #endif #include <stdlib.h> -#include <sys/stat.h> #include <stdio.h> #include <errno.h> #include <string.h> -#include <fcntl.h> #include <unistd.h> -#include <limits.h> #include <jack/jack.h> #include <pulse/xmalloc.h> -#include <pulsecore/core-error.h> #include <pulsecore/sink.h> #include <pulsecore/module.h> #include <pulsecore/core-util.h> @@ -68,7 +64,7 @@ PA_MODULE_LOAD_ONCE(TRUE); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_USAGE( "sink_name=<name for the sink> " - "sink_properties=<properties for the card> " + "sink_properties=<properties for the card> " "server_name=<jack server name> " "client_name=<jack client name> " "channels=<number of channels> " @@ -147,6 +143,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_sample_spec ss; /* Humm, we're not RUNNING, hence let's write some silence */ + /* This can happen if we're paused, or during shutdown (when we're unlinked but jack is still running). */ ss = u->sink->sample_spec; ss.channels = 1; @@ -197,6 +194,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse return pa_sink_process_msg(o, code, data, offset, memchunk); } +/* JACK Callback: This is called when JACK needs some data */ static int jack_process(jack_nframes_t nframes, void *arg) { struct userdata *u = arg; unsigned c; @@ -225,7 +223,6 @@ static void thread_func(void *userdata) { pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; @@ -251,6 +248,7 @@ finish: pa_log_debug("Thread shutting down"); } +/* JACK Callback: This is called when JACK triggers an error */ static void jack_error_func(const char*t) { char *s; @@ -259,6 +257,7 @@ static void jack_error_func(const char*t) { pa_xfree(s); } +/* JACK Callback: This is called when JACK is set up */ static void jack_init(void *arg) { struct userdata *u = arg; @@ -268,6 +267,7 @@ static void jack_init(void *arg) { pa_make_realtime(u->core->realtime_priority+4); } +/* JACK Callback: This is called when JACK kicks us */ static void jack_shutdown(void* arg) { struct userdata *u = arg; @@ -275,6 +275,7 @@ static void jack_shutdown(void* arg) { pa_asyncmsgq_post(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ON_SHUTDOWN, NULL, 0, NULL, NULL); } +/* JACK Callback: This is called when JACK changes the buffer size */ static int jack_buffer_size(jack_nframes_t nframes, void *arg) { struct userdata *u = arg; @@ -335,11 +336,12 @@ int pa__init(pa_module*m) { goto fail; } - ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput); + ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsInput); channels = 0; - for (p = ports; *p; p++) - channels++; + if (ports) + for (p = ports; *p; p++) + channels++; if (!channels) channels = m->core->default_sample_spec.channels; @@ -414,7 +416,7 @@ int pa__init(pa_module*m) { jack_set_thread_init_callback(u->client, jack_init, u); jack_set_buffer_size_callback(u->client, jack_buffer_size, u); - if (!(u->thread = pa_thread_new(thread_func, u))) { + if (!(u->thread = pa_thread_new("jack-sink", thread_func, u))) { pa_log("Failed to create thread."); goto fail; } @@ -427,7 +429,7 @@ int pa__init(pa_module*m) { if (do_connect) { for (i = 0, p = ports; i < ss.channels; i++, p++) { - if (!*p) { + if (!p || !*p) { pa_log("Not enough physical output ports, leaving unconnected."); break; } @@ -443,7 +445,8 @@ int pa__init(pa_module*m) { pa_sink_put(u->sink); - free(ports); + if (ports) + jack_free(ports); pa_modargs_free(ma); return 0; @@ -452,7 +455,8 @@ fail: if (ma) pa_modargs_free(ma); - free(ports); + if (ports) + jack_free(ports); pa__done(m); @@ -476,12 +480,12 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; - if (u->client) - jack_client_close(u->client); - if (u->sink) pa_sink_unlink(u->sink); + if (u->client) + jack_client_close(u->client); + if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); diff --git a/src/modules/jack/module-jack-source.c b/src/modules/jack/module-jack-source.c index ef89a98e..13109f3e 100644 --- a/src/modules/jack/module-jack-source.c +++ b/src/modules/jack/module-jack-source.c @@ -24,19 +24,15 @@ #endif #include <stdlib.h> -#include <sys/stat.h> #include <stdio.h> #include <errno.h> #include <string.h> -#include <fcntl.h> #include <unistd.h> -#include <limits.h> #include <jack/jack.h> #include <pulse/xmalloc.h> -#include <pulsecore/core-error.h> #include <pulsecore/source.h> #include <pulsecore/module.h> #include <pulsecore/core-util.h> @@ -196,7 +192,6 @@ static void thread_func(void *userdata) { pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); - pa_rtpoll_install(u->rtpoll); for (;;) { int ret; @@ -287,11 +282,12 @@ int pa__init(pa_module*m) { goto fail; } - ports = jack_get_ports(u->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput); + ports = jack_get_ports(u->client, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortIsPhysical|JackPortIsOutput); channels = 0; - for (p = ports; *p; p++) - channels++; + if (ports) + for (p = ports; *p; p++) + channels++; if (!channels) channels = m->core->default_sample_spec.channels; @@ -364,7 +360,7 @@ int pa__init(pa_module*m) { jack_on_shutdown(u->client, jack_shutdown, u); jack_set_thread_init_callback(u->client, jack_init, u); - if (!(u->thread = pa_thread_new(thread_func, u))) { + if (!(u->thread = pa_thread_new("jack-source", thread_func, u))) { pa_log("Failed to create thread."); goto fail; } @@ -377,7 +373,7 @@ int pa__init(pa_module*m) { if (do_connect) { for (i = 0, p = ports; i < ss.channels; i++, p++) { - if (!*p) { + if (!p || !*p) { pa_log("Not enough physical output ports, leaving unconnected."); break; } @@ -394,7 +390,8 @@ int pa__init(pa_module*m) { pa_source_put(u->source); - free(ports); + if (ports) + jack_free(ports); pa_modargs_free(ma); return 0; @@ -403,7 +400,8 @@ fail: if (ma) pa_modargs_free(ma); - free(ports); + if (ports) + jack_free(ports); pa__done(m); @@ -426,12 +424,12 @@ void pa__done(pa_module*m) { if (!(u = m->userdata)) return; - if (u->client) - jack_client_close(u->client); - if (u->source) pa_source_unlink(u->source); + if (u->client) + jack_client_close(u->client); + if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); diff --git a/src/modules/jack/module-jackdbus-detect.c b/src/modules/jack/module-jackdbus-detect.c new file mode 100644 index 00000000..864f96b1 --- /dev/null +++ b/src/modules/jack/module-jackdbus-detect.c @@ -0,0 +1,298 @@ +/*** + This file is part of PulseAudio. + + Written by David Henningsson <david.henningsson@canonical.com> + Copyright 2010 Canonical Ltd. + + Some code taken from other parts of PulseAudio, these are + Copyright 2006-2009 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 + 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 <config.h> +#endif + +#include <pulse/xmalloc.h> + +#include <pulsecore/log.h> +#include <pulsecore/modargs.h> +#include <pulsecore/core-util.h> +#include <pulsecore/dbus-shared.h> + +#include "module-jackdbus-detect-symdef.h" + +PA_MODULE_AUTHOR("David Henningsson"); +PA_MODULE_DESCRIPTION("Adds JACK sink/source ports when JACK is started"); +PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_USAGE("connect=<connect ports?>"); + +#define JACK_SERVICE_NAME "org.jackaudio.service" +#define JACK_INTERFACE_NAME "org.jackaudio.JackControl" +#define JACK_INTERFACE_PATH "/org/jackaudio/Controller" + +#define SERVICE_FILTER \ + "type='signal'," \ + "sender='" DBUS_SERVICE_DBUS "'," \ + "interface='" DBUS_INTERFACE_DBUS "'," \ + "member='NameOwnerChanged'," \ + "arg0='" JACK_SERVICE_NAME "'" + +#define RUNNING_FILTER(_a) \ + "type='signal'," \ + "sender='" JACK_SERVICE_NAME "'," \ + "interface='" JACK_INTERFACE_NAME "'," \ + "member='" _a "'" + +static const char* const valid_modargs[] = { + "connect", + NULL +}; + +#define JACK_SS_SINK 0 +#define JACK_SS_SOURCE 1 +#define JACK_SS_COUNT 2 + +static const char* const modnames[JACK_SS_COUNT] = { + "module-jack-sink", + "module-jack-source" +}; + + +struct userdata { + pa_module *module; + pa_core *core; + pa_dbus_connection *connection; + pa_bool_t filter_added, match_added; + pa_bool_t is_service_started; + pa_bool_t autoconnect_ports; + /* Using index here protects us from module unloading without us knowing */ + int jack_module_index[JACK_SS_COUNT]; +}; + + +static void ensure_ports_stopped(struct userdata* u) { + int i; + pa_assert(u); + + for (i = 0; i < JACK_SS_COUNT; i++) + if (u->jack_module_index[i]) { + pa_module_unload_request_by_index(u->core, u->jack_module_index[i], TRUE); + u->jack_module_index[i] = 0; + pa_log_info("Stopped %s.", modnames[i]); + } +} + +static void ensure_ports_started(struct userdata* u) { + int i; + pa_assert(u); + + for (i = 0; i < JACK_SS_COUNT; i++) + if (!u->jack_module_index[i]) { + char* args; + pa_module* m; + args = pa_sprintf_malloc("connect=%s", pa_yes_no(u->autoconnect_ports)); + m = pa_module_load(u->core, modnames[i], args); + pa_xfree(args); + + if (m) { + pa_log_info("Successfully started %s.", modnames[i]); + u->jack_module_index[i] = m->index; + } + else + pa_log_info("Failed to start %s.", modnames[i]); + } +} + + +static pa_bool_t check_service_started(struct userdata* u) { + DBusError error; + DBusMessage *m = NULL, *reply = NULL; + pa_bool_t new_status = FALSE; + dbus_bool_t call_result; + pa_assert(u); + + dbus_error_init(&error); + + /* Just a safety check; it isn't such a big deal if the name disappears just after the call. */ + if (!dbus_bus_name_has_owner(pa_dbus_connection_get(u->connection), + JACK_SERVICE_NAME, &error)) { + pa_log_debug("jackdbus isn't running."); + goto finish; + } + + if (!(m = dbus_message_new_method_call(JACK_SERVICE_NAME, JACK_INTERFACE_PATH, JACK_INTERFACE_NAME, "IsStarted"))) { + pa_log("Failed to allocate IsStarted() method call."); + goto finish; + } + + if (!(reply = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) { + pa_log("IsStarted() call failed: %s: %s", error.name, error.message); + goto finish; + } + + if (!dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &call_result, DBUS_TYPE_INVALID)) { + pa_log("IsStarted() call return failed: %s: %s", error.name, error.message); + goto finish; + } + + new_status = call_result; + +finish: + if (m) + dbus_message_unref(m); + if (reply) + dbus_message_unref(reply); + + dbus_error_free(&error); + if (new_status) + ensure_ports_started(u); + else + ensure_ports_stopped(u); + u->is_service_started = new_status; + return new_status; +} + +static DBusHandlerResult dbus_filter_handler(DBusConnection *c, DBusMessage *s, void *userdata) { + struct userdata *u = NULL; + DBusError error; + + pa_assert(userdata); + u = ((pa_module*) userdata)->userdata; + pa_assert(u); + + dbus_error_init(&error); + + if (dbus_message_is_signal(s, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) { + const char *name, *old, *new; + if (!dbus_message_get_args(s, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old, + DBUS_TYPE_STRING, &new, + DBUS_TYPE_INVALID)) + goto finish; + if (strcmp(name, JACK_SERVICE_NAME)) + goto finish; + + ensure_ports_stopped(u); + check_service_started(u); + } + + else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStarted")) { + ensure_ports_stopped(u); + check_service_started(u); + } + + else if (dbus_message_is_signal(s, JACK_INTERFACE_NAME, "ServerStopped")) { + ensure_ports_stopped(u); + } + +finish: + dbus_error_free(&error); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +int pa__init(pa_module *m) { + DBusError error; + pa_dbus_connection *connection = NULL; + struct userdata *u = NULL; + pa_modargs *ma; + + pa_assert(m); + + dbus_error_init(&error); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->autoconnect_ports = TRUE; + + if (pa_modargs_get_value_boolean(ma, "connect", &u->autoconnect_ports) < 0) { + pa_log("Failed to parse connect= argument."); + goto fail; + } + + if (!(connection = pa_dbus_bus_get(m->core, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { + + if (connection) + pa_dbus_connection_unref(connection); + + pa_log_error("Unable to contact D-Bus session bus: %s: %s", error.name, error.message); + goto fail; + } + u->connection = connection; + + if (!dbus_connection_add_filter(pa_dbus_connection_get(connection), dbus_filter_handler, m, NULL)) { + pa_log_error("Unable to add D-Bus filter"); + goto fail; + } + u->filter_added = 1; + + if (pa_dbus_add_matches( + pa_dbus_connection_get(connection), &error, SERVICE_FILTER, + RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL) < 0) { + pa_log_error("Unable to subscribe to signals: %s: %s", error.name, error.message); + goto fail; + } + u->match_added = 1; + + check_service_started(u); + + return 0; + +fail: + if (ma) + pa_modargs_free(ma); + + dbus_error_free(&error); + pa__done(m); + + return -1; +} + +void pa__done(pa_module *m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + return; + + ensure_ports_stopped(u); + + if (u->match_added) { + pa_dbus_remove_matches( + pa_dbus_connection_get(u->connection), SERVICE_FILTER, + RUNNING_FILTER("ServerStarted"), RUNNING_FILTER("ServerStopped"), NULL); + } + + if (u->filter_added) { + dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), dbus_filter_handler, m); + } + + if (u->connection) { + pa_dbus_connection_unref(u->connection); + } + + pa_xfree(u); +} |
