This prepares for loading tunnel sink and source module instances from the tunnel manager. If the tunnel manager is available, and the tunnel manager has sample spec and channel map information for the device, then let's use that information when creating the sink or source so that the tunnel will automatically match the parameters of the remote device. The remote device's proplist is copied to the tunnel too. --- src/Makefile.am | 4 +- src/modules/module-tunnel-sink-new.c | 154 ++++++++++++++++++++++++++++---- src/modules/module-tunnel-source-new.c | 155 +++++++++++++++++++++++++++++---- 3 files changed, 277 insertions(+), 36 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 2fc6403..97fc979 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1736,11 +1736,11 @@ module_match_la_LIBADD = $(MODULE_LIBADD) module_tunnel_sink_new_la_SOURCES = modules/module-tunnel-sink-new.c module_tunnel_sink_new_la_LDFLAGS = $(MODULE_LDFLAGS) -module_tunnel_sink_new_la_LIBADD = $(MODULE_LIBADD) +module_tunnel_sink_new_la_LIBADD = $(MODULE_LIBADD) libtunnel-manager.la module_tunnel_source_new_la_SOURCES = modules/module-tunnel-source-new.c module_tunnel_source_new_la_LDFLAGS = $(MODULE_LDFLAGS) -module_tunnel_source_new_la_LIBADD = $(MODULE_LIBADD) +module_tunnel_source_new_la_LIBADD = $(MODULE_LIBADD) libtunnel-manager.la module_tunnel_sink_la_SOURCES = modules/module-tunnel.c module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS) $(X11_CFLAGS) diff --git a/src/modules/module-tunnel-sink-new.c b/src/modules/module-tunnel-sink-new.c index 7d3bd99..078a6fc 100644 --- a/src/modules/module-tunnel-sink-new.c +++ b/src/modules/module-tunnel-sink-new.c @@ -23,6 +23,8 @@ #include <config.h> #endif +#include <modules/tunnel-manager/tunnel-manager.h> + #include <pulse/context.h> #include <pulse/timeval.h> #include <pulse/xmalloc.h> @@ -87,8 +89,20 @@ struct userdata { char *cookie_file; char *remote_server; char *remote_sink_name; + pa_proplist *user_proplist; + + struct remote_device_data *remote_device_data; +}; + +struct remote_device_data { + struct userdata *userdata; + pa_tunnel_manager_remote_device *device; + pa_hook_slot *unlinked_slot; + pa_hook_slot *proplist_changed_slot; }; +static void remote_device_data_free(struct remote_device_data *data); + static const char* const valid_modargs[] = { "sink_name", "sink_properties", @@ -103,6 +117,68 @@ static const char* const valid_modargs[] = { NULL, }; +static pa_hook_result_t remote_device_unlinked_cb(void *hook_data, void *call_data, void *userdata) { + struct remote_device_data *data = userdata; + + pa_assert(data); + + remote_device_data_free(data); + + return PA_HOOK_OK; +} + +static pa_hook_result_t remote_device_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) { + pa_tunnel_manager_remote_device *device = hook_data; + struct remote_device_data *data = userdata; + pa_proplist *proplist; + + pa_assert(device); + pa_assert(data); + + proplist = pa_proplist_copy(device->proplist); + + if (data->userdata->user_proplist) + pa_proplist_update(proplist, PA_UPDATE_REPLACE, data->userdata->user_proplist); + + pa_sink_update_proplist(data->userdata->sink, PA_UPDATE_SET, proplist); + pa_proplist_free(proplist); + + return PA_HOOK_OK; +} + +static void remote_device_data_new(struct userdata *u, pa_tunnel_manager_remote_device *device) { + struct remote_device_data *data; + + pa_assert(u); + pa_assert(device); + + data = pa_xnew0(struct remote_device_data, 1); + data->userdata = u; + data->device = device; + data->unlinked_slot = pa_hook_connect(&device->hooks[PA_TUNNEL_MANAGER_REMOTE_DEVICE_HOOK_UNLINKED], PA_HOOK_NORMAL, + remote_device_unlinked_cb, data); + data->proplist_changed_slot = pa_hook_connect(&device->hooks[PA_TUNNEL_MANAGER_REMOTE_DEVICE_HOOK_PROPLIST_CHANGED], + PA_HOOK_NORMAL, remote_device_proplist_changed_cb, data); + + pa_assert(!u->remote_device_data); + u->remote_device_data = data; +} + +static void remote_device_data_free(struct remote_device_data *data) { + pa_assert(data); + + if (data->userdata) + data->userdata->remote_device_data = NULL; + + if (data->proplist_changed_slot) + pa_hook_slot_free(data->proplist_changed_slot); + + if (data->unlinked_slot) + pa_hook_slot_free(data->unlinked_slot); + + pa_xfree(data); +} + static void cork_stream(struct userdata *u, bool cork) { pa_operation *operation; @@ -463,13 +539,6 @@ int pa__init(pa_module *m) { goto fail; } - ss = m->core->default_sample_spec; - map = m->core->default_channel_map; - if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { - pa_log("Invalid sample format specification or channel map"); - goto fail; - } - remote_server = pa_modargs_get_value(ma, "server", NULL); if (!remote_server) { pa_log("No server given!"); @@ -489,6 +558,41 @@ int pa__init(pa_module *m) { u->cookie_file = pa_xstrdup(pa_modargs_get_value(ma, "cookie", NULL)); u->remote_sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL)); + if (u->remote_sink_name) { + pa_tunnel_manager *manager; + + manager = pa_tunnel_manager_get(m->core, false); + if (manager) { + pa_tunnel_manager_remote_server *server; + void *state; + + PA_HASHMAP_FOREACH(server, manager->remote_servers, state) { + if (pa_streq(server->address, u->remote_server)) { + pa_tunnel_manager_remote_device *device; + + device = pa_hashmap_get(server->devices, u->remote_sink_name); + if (device) + remote_device_data_new(u, device); + + break; + } + } + } + } + + if (u->remote_device_data) { + ss = u->remote_device_data->device->sample_spec; + map = u->remote_device_data->device->channel_map; + } else { + ss = m->core->default_sample_spec; + map = m->core->default_channel_map; + } + + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Invalid sample format specification or channel map"); + goto fail; + } + u->thread_mq = pa_xnew0(pa_thread_mq, 1); pa_thread_mq_init_thread_mainloop(u->thread_mq, m->core->mainloop, u->thread_mainloop_api); @@ -504,17 +608,27 @@ 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); - pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "sound"); - pa_proplist_setf(sink_data.proplist, - PA_PROP_DEVICE_DESCRIPTION, - _("Tunnel to %s/%s"), - remote_server, - pa_strempty(u->remote_sink_name)); + if (u->remote_device_data) + pa_proplist_update(sink_data.proplist, PA_UPDATE_SET, u->remote_device_data->device->proplist); + else { + pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "sound"); + pa_proplist_setf(sink_data.proplist, + PA_PROP_DEVICE_DESCRIPTION, + _("Tunnel to %s/%s"), + remote_server, + pa_strempty(u->remote_sink_name)); + } + + if (pa_modargs_get_value(ma, "sink_properties", NULL)) { + u->user_proplist = pa_proplist_new(); - if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) { - pa_log("Invalid properties"); - pa_sink_new_data_done(&sink_data); - goto fail; + if (pa_modargs_get_proplist(ma, "sink_properties", u->user_proplist, PA_UPDATE_SET) < 0) { + pa_log("Invalid properties"); + pa_sink_new_data_done(&sink_data); + goto fail; + } + + pa_proplist_update(sink_data.proplist, PA_UPDATE_REPLACE, u->user_proplist); } if (!(u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY | PA_SINK_NETWORK))) { pa_log("Failed to create sink."); @@ -565,6 +679,12 @@ void pa__done(pa_module *m) { if (u->sink) pa_sink_unlink(u->sink); + if (u->remote_device_data) + remote_device_data_free(u->remote_device_data); + + if (u->user_proplist) + pa_proplist_free(u->user_proplist); + 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/module-tunnel-source-new.c b/src/modules/module-tunnel-source-new.c index c6580eb..4f0571c 100644 --- a/src/modules/module-tunnel-source-new.c +++ b/src/modules/module-tunnel-source-new.c @@ -23,6 +23,8 @@ #include <config.h> #endif +#include <modules/tunnel-manager/tunnel-manager.h> + #include <pulse/context.h> #include <pulse/timeval.h> #include <pulse/xmalloc.h> @@ -85,8 +87,20 @@ struct userdata { char *cookie_file; char *remote_server; char *remote_source_name; + pa_proplist *user_proplist; + + struct remote_device_data *remote_device_data; +}; + +struct remote_device_data { + struct userdata *userdata; + pa_tunnel_manager_remote_device *device; + pa_hook_slot *unlinked_slot; + pa_hook_slot *proplist_changed_slot; }; +static void remote_device_data_free(struct remote_device_data *data); + static const char* const valid_modargs[] = { "source_name", "source_properties", @@ -101,6 +115,68 @@ static const char* const valid_modargs[] = { NULL, }; +static pa_hook_result_t remote_device_unlinked_cb(void *hook_data, void *call_data, void *userdata) { + struct remote_device_data *data = userdata; + + pa_assert(data); + + remote_device_data_free(data); + + return PA_HOOK_OK; +} + +static pa_hook_result_t remote_device_proplist_changed_cb(void *hook_data, void *call_data, void *userdata) { + pa_tunnel_manager_remote_device *device = hook_data; + struct remote_device_data *data = userdata; + pa_proplist *proplist; + + pa_assert(device); + pa_assert(data); + + proplist = pa_proplist_copy(device->proplist); + + if (data->userdata->user_proplist) + pa_proplist_update(proplist, PA_UPDATE_REPLACE, data->userdata->user_proplist); + + pa_source_update_proplist(data->userdata->source, PA_UPDATE_SET, proplist); + pa_proplist_free(proplist); + + return PA_HOOK_OK; +} + +static void remote_device_data_new(struct userdata *u, pa_tunnel_manager_remote_device *device) { + struct remote_device_data *data; + + pa_assert(u); + pa_assert(device); + + data = pa_xnew0(struct remote_device_data, 1); + data->userdata = u; + data->device = device; + data->unlinked_slot = pa_hook_connect(&device->hooks[PA_TUNNEL_MANAGER_REMOTE_DEVICE_HOOK_UNLINKED], PA_HOOK_NORMAL, + remote_device_unlinked_cb, data); + data->proplist_changed_slot = pa_hook_connect(&device->hooks[PA_TUNNEL_MANAGER_REMOTE_DEVICE_HOOK_PROPLIST_CHANGED], + PA_HOOK_NORMAL, remote_device_proplist_changed_cb, data); + + pa_assert(!u->remote_device_data); + u->remote_device_data = data; +} + +static void remote_device_data_free(struct remote_device_data *data) { + pa_assert(data); + + if (data->userdata) + data->userdata->remote_device_data = NULL; + + if (data->proplist_changed_slot) + pa_hook_slot_free(data->proplist_changed_slot); + + if (data->unlinked_slot) + pa_hook_slot_free(data->unlinked_slot); + + pa_xfree(data); +} + static void cork_stream(struct userdata *u, bool cork) { pa_operation *operation; @@ -471,13 +547,6 @@ int pa__init(pa_module *m) { goto fail; } - ss = m->core->default_sample_spec; - map = m->core->default_channel_map; - if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { - pa_log("Invalid sample format specification or channel map"); - goto fail; - } - remote_server = pa_modargs_get_value(ma, "server", NULL); if (!remote_server) { pa_log("No server given!"); @@ -497,6 +566,41 @@ int pa__init(pa_module *m) { u->cookie_file = pa_xstrdup(pa_modargs_get_value(ma, "cookie", NULL)); u->remote_source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL)); + if (u->remote_source_name) { + pa_tunnel_manager *manager; + + manager = pa_tunnel_manager_get(m->core, false); + if (manager) { + pa_tunnel_manager_remote_server *server; + void *state; + + PA_HASHMAP_FOREACH(server, manager->remote_servers, state) { + if (pa_streq(server->address, u->remote_server)) { + pa_tunnel_manager_remote_device *device; + + device = pa_hashmap_get(server->devices, u->remote_source_name); + if (device) + remote_device_data_new(u, device); + + break; + } + } + } + } + + if (u->remote_device_data) { + ss = u->remote_device_data->device->sample_spec; + map = u->remote_device_data->device->channel_map; + } else { + ss = m->core->default_sample_spec; + map = m->core->default_channel_map; + } + + if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Invalid sample format specification or channel map"); + goto fail; + } + u->thread_mq = pa_xnew0(pa_thread_mq, 1); pa_thread_mq_init_thread_mainloop(u->thread_mq, m->core->mainloop, u->thread_mainloop_api); @@ -512,18 +616,29 @@ int pa__init(pa_module *m) { pa_source_new_data_set_sample_spec(&source_data, &ss); pa_source_new_data_set_channel_map(&source_data, &map); - pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "sound"); - pa_proplist_setf(source_data.proplist, - PA_PROP_DEVICE_DESCRIPTION, - _("Tunnel to %s/%s"), - remote_server, - pa_strempty(u->remote_source_name)); + if (u->remote_device_data) + pa_proplist_update(source_data.proplist, PA_UPDATE_SET, u->remote_device_data->device->proplist); + else { + pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "sound"); + pa_proplist_setf(source_data.proplist, + PA_PROP_DEVICE_DESCRIPTION, + _("Tunnel to %s/%s"), + remote_server, + pa_strempty(u->remote_source_name)); + } - if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) { - pa_log("Invalid properties"); - pa_source_new_data_done(&source_data); - goto fail; + if (pa_modargs_get_value(ma, "source_properties", NULL)) { + u->user_proplist = pa_proplist_new(); + + if (pa_modargs_get_proplist(ma, "source_properties", u->user_proplist, PA_UPDATE_SET) < 0) { + pa_log("Invalid properties"); + pa_source_new_data_done(&source_data); + goto fail; + } + + pa_proplist_update(source_data.proplist, PA_UPDATE_REPLACE, u->user_proplist); } + if (!(u->source = pa_source_new(m->core, &source_data, PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY | PA_SOURCE_NETWORK))) { pa_log("Failed to create source."); pa_source_new_data_done(&source_data); @@ -571,6 +686,12 @@ void pa__done(pa_module *m) { if (u->source) pa_source_unlink(u->source); + if (u->remote_device_data) + remote_device_data_free(u->remote_device_data); + + if (u->user_proplist) + pa_proplist_free(u->user_proplist); + if (u->thread) { pa_asyncmsgq_send(u->thread_mq->inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); -- 1.9.3