diff --git a/spa/include/spa/graph-scheduler3.h b/spa/include/spa/graph-scheduler3.h index b04a41dcb..98abb32fc 100644 --- a/spa/include/spa/graph-scheduler3.h +++ b/spa/include/spa/graph-scheduler3.h @@ -90,7 +90,8 @@ static inline void spa_graph_scheduler_pull(struct spa_graph_scheduler *sched, s pnode = pport->node; debug("node %p peer %p io %d\n", node, pnode, pport->io->status); if (pport->io->status == SPA_RESULT_NEED_BUFFER) { - spa_list_insert(ready.prev, &pnode->ready_link); + if (pnode->ready_link.next == NULL) + spa_list_append(&ready, &pnode->ready_link); } else if (pport->io->status == SPA_RESULT_OK && !(pnode->flags & SPA_GRAPH_NODE_FLAG_ASYNC)) node->ready_in++; @@ -111,7 +112,7 @@ static inline void spa_graph_scheduler_pull(struct spa_graph_scheduler *sched, s n->ready_link.next = NULL; } - debug("node %p %d %d\n", node, node->ready_in, node->required_in); + debug("node %p ready_in:%d required_in:%d\n", node, node->ready_in, node->required_in); if (node->required_in > 0 && node->ready_in == node->required_in) { node->state = node->callbacks->process_input(node->callbacks_data); @@ -179,7 +180,8 @@ static inline void spa_graph_scheduler_push(struct spa_graph_scheduler *sched, s pnode->ready_in, pnode->required_in); if (pnode->required_in > 0 && pnode->ready_in == pnode->required_in) - spa_list_insert(ready.prev, &pnode->ready_link); + if (pnode->ready_link.next == NULL) + spa_list_append(&ready, &pnode->ready_link); } spa_graph_scheduler_chain(sched, &ready); diff --git a/spa/include/spa/loop.h b/spa/include/spa/loop.h index 235330a39..be135a21c 100644 --- a/spa/include/spa/loop.h +++ b/spa/include/spa/loop.h @@ -148,19 +148,11 @@ struct spa_loop_control { #define spa_loop_control_leave(l) (l)->leave(l) -typedef void (*spa_source_io_func_t) (struct spa_loop_utils *utils, - struct spa_source *source, - int fd, enum spa_io mask, void *data); -typedef void (*spa_source_idle_func_t) (struct spa_loop_utils *utils, - struct spa_source *source, void *data); -typedef void (*spa_source_event_func_t) (struct spa_loop_utils *utils, - struct spa_source *source, - uint64_t count, void *data); -typedef void (*spa_source_timer_func_t) (struct spa_loop_utils *utils, - struct spa_source *source, void *data); -typedef void (*spa_source_signal_func_t) (struct spa_loop_utils *utils, - struct spa_source *source, - int signal_number, void *data); +typedef void (*spa_source_io_func_t) (void *data, int fd, enum spa_io mask); +typedef void (*spa_source_idle_func_t) (void *data); +typedef void (*spa_source_event_func_t) (void *data, uint64_t count); +typedef void (*spa_source_timer_func_t) (void *data, uint64_t expirations); +typedef void (*spa_source_signal_func_t) (void *data, int signal_number); /** * struct spa_loop_utils: diff --git a/spa/plugins/support/loop.c b/spa/plugins/support/loop.c index 0ac25238a..0f20194c6 100644 --- a/spa/plugins/support/loop.c +++ b/spa/plugins/support/loop.c @@ -263,7 +263,7 @@ loop_invoke(struct spa_loop *loop, return res; } -static void wakeup_func(struct spa_loop_utils *utils, struct spa_source *source, uint64_t count, void *data) +static void wakeup_func(void *data, uint64_t count) { struct impl *impl = data; uint32_t index; @@ -357,7 +357,7 @@ static int loop_iterate(struct spa_loop_control *ctrl, int timeout) static void source_io_func(struct spa_source *source) { struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); - impl->func.io(&impl->impl->utils, source, source->fd, source->rmask, source->data); + impl->func.io(source->data, source->fd, source->rmask); } static struct spa_source *loop_add_io(struct spa_loop_utils *utils, @@ -398,7 +398,7 @@ static int loop_update_io(struct spa_source *source, enum spa_io mask) static void source_idle_func(struct spa_source *source) { struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); - impl->func.idle(&impl->impl->utils, source, source->data); + impl->func.idle(source->data); } static struct spa_source *loop_add_idle(struct spa_loop_utils *utils, @@ -457,7 +457,7 @@ static void source_event_func(struct spa_source *source) spa_log_warn(impl->impl->log, NAME " %p: failed to read event fd %d: %s", source, source->fd, strerror(errno)); - impl->func.event(&impl->impl->utils, source, count, source->data); + impl->func.event(source->data, count); } static struct spa_source *loop_add_event(struct spa_loop_utils *utils, @@ -499,13 +499,13 @@ static void loop_signal_event(struct spa_source *source) static void source_timer_func(struct spa_source *source) { struct source_impl *impl = SPA_CONTAINER_OF(source, struct source_impl, source); - uint64_t expires; + uint64_t expirations; - if (read(source->fd, &expires, sizeof(uint64_t)) != sizeof(uint64_t)) + if (read(source->fd, &expirations, sizeof(uint64_t)) != sizeof(uint64_t)) spa_log_warn(impl->impl->log, NAME " %p: failed to read timer fd %d: %s", source, source->fd, strerror(errno)); - impl->func.timer(&impl->impl->utils, source, source->data); + impl->func.timer(source->data, expirations); } static struct spa_source *loop_add_timer(struct spa_loop_utils *utils, @@ -568,7 +568,7 @@ static void source_signal_func(struct spa_source *source) spa_log_warn(impl->impl->log, NAME " %p: failed to read signal fd %d: %s", source, source->fd, strerror(errno)); - impl->func.signal(&impl->impl->utils, source, impl->signal_number, source->data); + impl->func.signal(source->data, impl->signal_number); } static struct spa_source *loop_add_signal(struct spa_loop_utils *utils, diff --git a/src/examples/export-sink.c b/src/examples/export-sink.c index dac782df8..ffdb0e19c 100644 --- a/src/examples/export-sink.c +++ b/src/examples/export-sink.c @@ -434,7 +434,7 @@ static void make_node(struct data *data) data->node = pw_node_new(data->core, NULL, NULL, "SDL-sink", props, 0); pw_node_set_implementation(data->node, &impl_node, data); - data->port = pw_port_new(PW_DIRECTION_INPUT, 0, 0); + data->port = pw_port_new(PW_DIRECTION_INPUT, 0, NULL, 0); pw_port_set_implementation(data->port, &impl_port, data); pw_port_add(data->port, data->node); pw_node_register(data->node); diff --git a/src/examples/local-v4l2.c b/src/examples/local-v4l2.c index 595180e14..512cd2b85 100644 --- a/src/examples/local-v4l2.c +++ b/src/examples/local-v4l2.c @@ -411,7 +411,7 @@ static void make_nodes(struct data *data) data->node = pw_node_new(data->core, NULL, NULL, "SDL-sink", NULL, 0); pw_node_set_implementation(data->node, &impl_node, data); - data->port = pw_port_new(PW_DIRECTION_INPUT, 0, 0); + data->port = pw_port_new(PW_DIRECTION_INPUT, 0, NULL, 0); pw_port_set_implementation(data->port, &impl_port, data); pw_port_add(data->port, data->node); pw_node_register(data->node); @@ -427,7 +427,8 @@ static void make_nodes(struct data *data) data->port, NULL, NULL, - NULL); + NULL, + 0); pw_link_activate(data->link); } diff --git a/src/examples/video-src.c b/src/examples/video-src.c index 77c6f36f9..569875e29 100644 --- a/src/examples/video-src.c +++ b/src/examples/video-src.c @@ -79,7 +79,7 @@ struct data { uint32_t seq; }; -static void on_timeout(struct spa_loop_utils *utils, struct spa_source *source, void *userdata) +static void on_timeout(void *userdata, uint64_t expirations) { struct data *data = userdata; uint32_t id; diff --git a/src/modules/meson.build b/src/modules/meson.build index 6522cf20b..ccd08e7d5 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -72,7 +72,8 @@ pipewire_module_protocol_native = shared_library('pipewire-module-protocol-nativ if jack_dep.found() pipewire_module_jack = shared_library('pipewire-module-jack', [ 'module-jack.c', - 'module-jack/shm.c' ], + 'module-jack/shm.c', + 'module-jack/jack-node.c' ], c_args : pipewire_module_c_args, include_directories : [configinc, spa_inc], link_with : spalib, diff --git a/src/modules/module-autolink.c b/src/modules/module-autolink.c index a468dd565..f5e41b43e 100644 --- a/src/modules/module-autolink.c +++ b/src/modules/module-autolink.c @@ -47,6 +47,13 @@ struct node_info { struct pw_node *node; struct spa_hook node_listener; + struct spa_list links; +}; + +struct link_data { + struct spa_list l; + + struct node_info *node_info; struct pw_link *link; struct spa_hook link_listener; }; @@ -62,11 +69,21 @@ static struct node_info *find_node_info(struct impl *impl, struct pw_node *node) return NULL; } +static void link_data_free(struct link_data *data) +{ + spa_list_remove(&data->l); + spa_hook_remove(&data->link_listener); + free(data); +} + static void node_info_free(struct node_info *info) { + struct link_data *ld, *t; + spa_list_remove(&info->l); spa_hook_remove(&info->node_listener); - spa_hook_remove(&info->link_listener); + spa_list_for_each_safe(ld, t, &info->links, l) + link_data_free(ld); free(info); } @@ -75,8 +92,9 @@ static void try_link_port(struct pw_node *node, struct pw_port *port, struct nod static void link_port_unlinked(void *data, struct pw_port *port) { - struct node_info *info = data; - struct pw_link *link = info->link; + struct link_data *ld = data; + struct node_info *info = ld->node_info; + struct pw_link *link = ld->link; struct impl *impl = info->impl; struct pw_port *input = pw_link_get_input(link); @@ -89,8 +107,9 @@ link_port_unlinked(void *data, struct pw_port *port) static void link_state_changed(void *data, enum pw_link_state old, enum pw_link_state state, const char *error) { - struct node_info *info = data; - struct pw_link *link = info->link; + struct link_data *ld = data; + struct node_info *info = ld->node_info; + struct pw_link *link = ld->link; struct impl *impl = info->impl; switch (state) { @@ -114,13 +133,9 @@ link_state_changed(void *data, enum pw_link_state old, enum pw_link_state state, static void link_destroy(void *data) { - struct node_info *info = data; - struct pw_link *link = info->link; - struct impl *impl = info->impl; - - pw_log_debug("module %p: link %p destroyed", impl, link); - spa_hook_remove(&info->link_listener); - spa_list_init(&info->link_listener.link); + struct link_data *ld = data; + pw_log_debug("module %p: link %p destroyed", ld->node_info->impl, ld->link); + link_data_free(ld); } static const struct pw_link_events link_events = { @@ -133,18 +148,15 @@ static const struct pw_link_events link_events = { static void try_link_port(struct pw_node *node, struct pw_port *port, struct node_info *info) { struct impl *impl = info->impl; - struct pw_properties *props; + const struct pw_properties *props; const char *str; uint32_t path_id; char *error = NULL; struct pw_link *link; struct pw_port *target; + struct link_data *ld; props = pw_node_get_properties(node); - if (props == NULL) { - pw_log_debug("module %p: node has no properties", impl); - return; - } str = pw_properties_get(props, "pipewire.target.node"); if (str != NULL) @@ -170,13 +182,22 @@ static void try_link_port(struct pw_node *node, struct pw_port *port, struct nod port = tmp; } - link = pw_link_new(impl->core, pw_module_get_global(impl->module), port, target, NULL, NULL, &error); + link = pw_link_new(impl->core, + pw_module_get_global(impl->module), + port, target, + NULL, NULL, + &error, + sizeof(struct link_data)); if (link == NULL) goto error; - info->link = link; + ld = pw_link_get_user_data(link); + ld->link = link; + ld->node_info = info; + pw_link_add_listener(link, &ld->link_listener, &link_events, ld); + + spa_list_append(&info->links, &ld->l); - pw_link_add_listener(link, &info->link_listener, &link_events, info); pw_link_activate(link); return; @@ -242,11 +263,10 @@ core_global_added(void *data, struct pw_global *global) ninfo = calloc(1, sizeof(struct node_info)); ninfo->impl = impl; ninfo->node = node; + spa_list_init(&ninfo->links); - spa_list_insert(impl->node_list.prev, &ninfo->l); - + spa_list_append(&impl->node_list, &ninfo->l); pw_node_add_listener(node, &ninfo->node_listener, &node_events, ninfo); - spa_list_init(&ninfo->link_listener.link); pw_log_debug("module %p: node %p added", impl, node); diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c index 68c478d43..aff27b035 100644 --- a/src/modules/module-flatpak.c +++ b/src/modules/module-flatpak.c @@ -531,12 +531,12 @@ const struct pw_core_events core_events = { .global_removed = core_global_removed, }; -static void dispatch_cb(struct spa_loop_utils *utils, struct spa_source *source, void *userdata) +static void dispatch_cb(void *userdata) { struct impl *impl = userdata; if (dbus_connection_dispatch(impl->bus) == DBUS_DISPATCH_COMPLETE) - pw_loop_enable_idle(pw_core_get_main_loop(impl->core), source, false); + pw_loop_enable_idle(pw_core_get_main_loop(impl->core), impl->dispatch_event, false); } static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) @@ -583,8 +583,7 @@ static inline unsigned int io_to_dbus(enum spa_io mask) } static void -handle_io_event(struct spa_loop_utils *utils, - struct spa_source *source, int fd, enum spa_io mask, void *userdata) +handle_io_event(void *userdata, int fd, enum spa_io mask) { DBusWatch *watch = userdata; @@ -630,20 +629,26 @@ static void toggle_watch(DBusWatch *watch, void *userdata) pw_loop_update_io(pw_core_get_main_loop(impl->core), source, dbus_to_io(watch)); } +struct timeout_data { + struct spa_source *source; + struct impl *impl; +}; static void -handle_timer_event(struct spa_loop_utils *utils, struct spa_source *source, void *userdata) +handle_timer_event(void *userdata, uint64_t expirations) { DBusTimeout *timeout = userdata; uint64_t t; struct timespec ts; + struct timeout_data *data = dbus_timeout_get_data(timeout); + struct impl *impl = data->impl; if (dbus_timeout_get_enabled(timeout)) { t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC; ts.tv_sec = t / SPA_NSEC_PER_SEC; ts.tv_nsec = t % SPA_NSEC_PER_SEC; - spa_loop_utils_update_timer(utils, source, &ts, NULL, false); - + pw_loop_update_timer(pw_core_get_main_loop(impl->core), + data->source, &ts, NULL, false); dbus_timeout_handle(timeout); } } @@ -651,40 +656,44 @@ handle_timer_event(struct spa_loop_utils *utils, struct spa_source *source, void static dbus_bool_t add_timeout(DBusTimeout *timeout, void *userdata) { struct impl *impl = userdata; - struct spa_source *source; struct timespec ts; + struct timeout_data *data; uint64_t t; if (!dbus_timeout_get_enabled(timeout)) return FALSE; - source = pw_loop_add_timer(pw_core_get_main_loop(impl->core), handle_timer_event, timeout); - - dbus_timeout_set_data(timeout, source, NULL); + data = calloc(1, sizeof(struct timeout_data)); + data->impl = impl; + data->source = pw_loop_add_timer(pw_core_get_main_loop(impl->core), handle_timer_event, timeout); + dbus_timeout_set_data(timeout, data, NULL); t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC; ts.tv_sec = t / SPA_NSEC_PER_SEC; ts.tv_nsec = t % SPA_NSEC_PER_SEC; - pw_loop_update_timer(pw_core_get_main_loop(impl->core), source, &ts, NULL, false); + pw_loop_update_timer(pw_core_get_main_loop(impl->core), data->source, &ts, NULL, false); + return TRUE; } static void remove_timeout(DBusTimeout *timeout, void *userdata) { struct impl *impl = userdata; - struct spa_source *source; + struct timeout_data *data; - if ((source = dbus_timeout_get_data(timeout))) - pw_loop_destroy_source(pw_core_get_main_loop(impl->core), source); + if ((data = dbus_timeout_get_data(timeout))) { + pw_loop_destroy_source(pw_core_get_main_loop(impl->core), data->source); + free(data); + } } static void toggle_timeout(DBusTimeout *timeout, void *userdata) { struct impl *impl = userdata; - struct spa_source *source; + struct timeout_data *data; struct timespec ts, *tsp; - source = dbus_timeout_get_data(timeout); + data = dbus_timeout_get_data(timeout); if (dbus_timeout_get_enabled(timeout)) { uint64_t t = dbus_timeout_get_interval(timeout) * SPA_NSEC_PER_MSEC; @@ -694,7 +703,7 @@ static void toggle_timeout(DBusTimeout *timeout, void *userdata) } else { tsp = NULL; } - pw_loop_update_timer(pw_core_get_main_loop(impl->core), source, tsp, NULL, false); + pw_loop_update_timer(pw_core_get_main_loop(impl->core), data->source, tsp, NULL, false); } static void wakeup_main(void *userdata) diff --git a/src/modules/module-jack.c b/src/modules/module-jack.c index 7650dba9a..3c5b3e46c 100644 --- a/src/modules/module-jack.c +++ b/src/modules/module-jack.c @@ -27,10 +27,6 @@ #include #include #include -#include - -#include -#include #include "config.h" @@ -48,11 +44,8 @@ #include "pipewire/data-loop.h" #include "pipewire/main-loop.h" -#include "modules/module-jack/defs.h" -#include "modules/module-jack/shm.h" -#include "modules/module-jack/shared.h" -#include "modules/module-jack/synchro.h" -#include "modules/module-jack/server.h" +#include "modules/module-jack/jack.h" +#include "modules/module-jack/jack-node.h" #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 108 @@ -72,7 +65,6 @@ struct socket { struct pw_loop *loop; struct spa_source *source; - char *core_name; struct spa_list link; }; @@ -82,6 +74,8 @@ struct impl { struct pw_module *module; struct spa_list link; + struct spa_source *timer; + struct pw_properties *properties; struct spa_list socket_list; @@ -90,6 +84,8 @@ struct impl { struct spa_loop_control_hooks hooks; struct jack_server server; + + struct pw_link *sink_link; }; struct client { @@ -101,6 +97,31 @@ struct client { struct spa_source *source; }; +static bool init_socket_name(struct sockaddr_un *addr, const char *name, bool promiscuous, int which) +{ + int name_size; + const char *runtime_dir; + + runtime_dir = JACK_SOCKET_DIR; + + addr->sun_family = AF_UNIX; + if (promiscuous) { + name_size = snprintf(addr->sun_path, sizeof(addr->sun_path), + "%s/jack_%s_%d", runtime_dir, name, which) + 1; + } else { + name_size = snprintf(addr->sun_path, sizeof(addr->sun_path), + "%s/jack_%s_%d_%d", runtime_dir, name, getuid(), which) + 1; + } + + if (name_size > (int) sizeof(addr->sun_path)) { + pw_log_error("socket path \"%s/%s\" plus null terminator exceeds 108 bytes", + runtime_dir, name); + *addr->sun_path = 0; + return false; + } + return true; +} + static int process_messages(struct client *client); static void client_destroy(void *data) @@ -116,6 +137,10 @@ static void client_destroy(void *data) static int handle_register_port(struct client *client) { + struct impl *impl = client->impl; + struct jack_server *server = &impl->server; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_connection_manager *conn; int result = 0; int ref_num; char name[JACK_PORT_NAME_SIZE + 1]; @@ -123,6 +148,8 @@ handle_register_port(struct client *client) unsigned int flags; unsigned int buffer_size; static jack_port_id_t port_index = 0; + jack_port_type_id_t type_id; + struct jack_client *jc; CheckSize(kRegisterPort_size); CheckRead(&ref_num, sizeof(int)); @@ -131,10 +158,45 @@ handle_register_port(struct client *client) CheckRead(&flags, sizeof(unsigned int)); CheckRead(&buffer_size, sizeof(unsigned int)); - pw_log_error("protocol-jack %p: kRegisterPort %d %s %s %u %u", client->impl, + pw_log_debug("protocol-jack %p: kRegisterPort %d %s %s %u %u", impl, ref_num, name, port_type, flags, buffer_size); - port_index++; + type_id = jack_port_get_type_id(port_type); + + if (jack_graph_manager_find_port(mgr, name) != NO_PORT) { + pw_log_error("protocol-jack %p: port_name %s exists", impl, name); + result = -1; + goto reply; + } + + port_index = jack_graph_manager_allocate_port(mgr, ref_num, name, type_id, flags); + if (port_index == NO_PORT) { + pw_log_error("protocol-jack %p: failed to create port name %s", impl, name); + result = -1; + goto reply; + } + + jc = server->client_table[ref_num]; + pw_jack_node_add_port(jc->node, + flags & JackPortIsInput ? + PW_DIRECTION_INPUT : + PW_DIRECTION_OUTPUT, + port_index); + + conn = jack_graph_manager_next_start(mgr); + + if (jack_connection_manager_add_port(conn, (flags & JackPortIsInput) ? true : false, + ref_num, port_index) < 0) { + pw_log_error("protocol-jack %p: failed to add port", impl); + jack_graph_manager_release_port(mgr, port_index); + result = -1; + goto reply_stop; + } + + reply_stop: + jack_graph_manager_next_stop(mgr); + + reply: CheckWrite(&result, sizeof(int)); CheckWrite(&port_index, sizeof(jack_port_id_t)); return 0; @@ -143,6 +205,10 @@ handle_register_port(struct client *client) static int handle_activate_client(struct client *client) { + struct impl *impl = client->impl; + struct jack_server *server = &impl->server; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_connection_manager *conn; int result = 0; int ref_num; int is_real_time; @@ -151,9 +217,16 @@ handle_activate_client(struct client *client) CheckRead(&ref_num, sizeof(int)); CheckRead(&is_real_time, sizeof(int)); - pw_log_error("protocol-jack %p: kActivateClient %d %d", client->impl, + pw_log_debug("protocol-jack %p: kActivateClient %d %d", client->impl, ref_num, is_real_time); + conn = jack_graph_manager_next_start(mgr); + + jack_connection_manager_direct_connect(conn, server->freewheel_ref_num, ref_num); + jack_connection_manager_direct_connect(conn, ref_num, server->freewheel_ref_num); + + jack_graph_manager_next_stop(mgr); + CheckWrite(&result, sizeof(int)); return 0; } @@ -161,15 +234,31 @@ handle_activate_client(struct client *client) static int handle_deactivate_client(struct client *client) { + struct impl *impl = client->impl; + struct jack_server *server = &impl->server; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_connection_manager *conn; int result = 0; - int ref_num; + int ref_num, fw_ref; CheckSize(kDeactivateClient_size); CheckRead(&ref_num, sizeof(int)); - pw_log_error("protocol-jack %p: kDeactivateClient %d", client->impl, + pw_log_debug("protocol-jack %p: kDeactivateClient %d", client->impl, ref_num); + fw_ref = server->freewheel_ref_num; + + conn = jack_graph_manager_next_start(mgr); + + if (jack_connection_manager_is_direct_connection(conn, fw_ref, ref_num)) + jack_connection_manager_direct_disconnect(conn, fw_ref, ref_num); + + if (jack_connection_manager_is_direct_connection(conn, ref_num, fw_ref)) + jack_connection_manager_direct_disconnect(conn, ref_num, fw_ref); + + jack_graph_manager_next_stop(mgr); + CheckWrite(&result, sizeof(int)); return 0; } @@ -192,7 +281,7 @@ handle_client_check(struct client *client) CheckRead(&UUID, sizeof(int)); CheckRead(&open, sizeof(int)); - pw_log_error("protocol-jack %p: kClientCheck %s %d %d %d %d", client->impl, + pw_log_debug("protocol-jack %p: kClientCheck %s %d %d %d %d", client->impl, name, protocol, options, UUID, open); status = 0; @@ -215,16 +304,74 @@ handle_client_check(struct client *client) return 0; } +static int +notify_client(struct jack_client *client, int ref_num, const char *name, int notify, + int sync, const char* message, int value1, int value2) +{ + int size, result = 0; + char _name[JACK_CLIENT_NAME_SIZE+1]; + char _message[JACK_MESSAGE_SIZE+1]; + + if (client->fd == 0) + return 0; + + snprintf(_name, sizeof(_name), "%s", name); + snprintf(_message, sizeof(_message), "%s", message); + + size = sizeof(int) + sizeof(_name) + 5 * sizeof(int) + sizeof(_message); + CheckWrite(&size, sizeof(int)); + CheckWrite(_name, sizeof(_name)); + CheckWrite(&ref_num, sizeof(int)); + CheckWrite(¬ify, sizeof(int)); + CheckWrite(&value1, sizeof(int)); + CheckWrite(&value2, sizeof(int)); + CheckWrite(&sync, sizeof(int)); + CheckWrite(_message, sizeof(_message)); + + if (sync) + CheckRead(&result, sizeof(int)); + + return result; +} + +static int +notify_add_client(struct impl *impl, struct jack_client *client, const char *name, int ref_num) +{ + struct jack_server *server = &impl->server; + int i; + + for (i = 0; i < CLIENT_NUM; i++) { + struct jack_client *c = server->client_table[i]; + const char *n; + + if (c == NULL || c == client) + continue; + + n = c->control->name; + if (notify_client(c, ref_num, name, jack_notify_AddClient, false, "", 0, 0) < 0) { + pw_log_warn("module-jack %p: can't notify client", impl); + } + if (notify_client(client, i, n, jack_notify_AddClient, true, "", 0, 0) < 0) { + pw_log_error("module-jack %p: can't notify client", impl); + return -1; + } + } + return 0; +} + static int handle_client_open(struct client *client) { struct impl *impl = client->impl; struct jack_server *server = &impl->server; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_connection_manager *conn; int PID, UUID; char name[JACK_CLIENT_NAME_SIZE+1]; - int result, ref_num, shared_engine, shared_client, shared_graph; + int result = -1, ref_num, shared_engine, shared_client, shared_graph; struct jack_client *jc; const struct ucred *ucred; + struct sockaddr_un addr; CheckSize(kClientOpen_size); CheckRead(&PID, sizeof(int)); @@ -233,7 +380,7 @@ handle_client_open(struct client *client) ref_num = jack_server_allocate_ref_num(server); if (ref_num == -1) { - result = -1; + pw_log_error("module-jack %p: can't allocated ref_num", impl); goto reply; } @@ -247,7 +394,20 @@ handle_client_open(struct client *client) 0, false, server->promiscuous) < 0) { - result = -1; + pw_log_error("module-jack %p: can't init synchro", impl); + goto reply; + } + + if ((jc->fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + pw_log_error("module-jack %p: can't create socket %s", impl, strerror(errno)); + goto reply; + } + + if (!init_socket_name(&addr, name, server->promiscuous, 0)) + goto reply; + + if (connect(jc->fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + pw_log_error("module-jack %p: can't connect socket %s", impl, strerror(errno)); goto reply; } @@ -255,17 +415,31 @@ handle_client_open(struct client *client) jc->control = jack_client_control_alloc(name, ucred ? ucred->pid : 0, ref_num, -1); if (jc->control == NULL) { - result = -1; + pw_log_error("module-jack %p: can't create control", impl); goto reply; } server->client_table[ref_num] = jc; + pw_log_debug("module-jack %p: Added client %d \"%s\"", impl, ref_num, name); + + conn = jack_graph_manager_next_start(mgr); + jack_connection_manager_init_ref_num(conn, ref_num); + jack_graph_manager_next_stop(mgr); + + jc->node = pw_jack_node_new(impl->core, pw_module_get_global(impl->module), + server, ref_num, NULL); + + if (notify_add_client(impl, jc, name, ref_num) < 0) { + pw_log_error("module-jack %p: can't notify add_client", impl); + goto reply; + } - result = 0; shared_engine = impl->server.engine_control->info.index; shared_client = jc->control->info.index; shared_graph = impl->server.graph_manager->info.index; + result = 0; + reply: CheckWrite(&result, sizeof(int)); CheckWrite(&shared_engine, sizeof(int)); @@ -290,16 +464,123 @@ handle_client_close(struct client *client) static int handle_connect_name_ports(struct client *client) { + struct impl *impl = client->impl; + struct jack_server *server = &impl->server; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_connection_manager *conn; + struct jack_client *jc; int ref_num; char src[REAL_JACK_PORT_NAME_SIZE+1]; char dst[REAL_JACK_PORT_NAME_SIZE+1]; - int result = 0; + int result = -1, in_ref, out_ref; + jack_port_id_t src_id, dst_id; + struct jack_port *src_port, *dst_port; + struct pw_port *out_port, *in_port; + struct pw_link *link; CheckSize(kConnectNamePorts_size); CheckRead(&ref_num, sizeof(int)); CheckRead(src, sizeof(src)); CheckRead(dst, sizeof(dst)); + src_id = jack_graph_manager_find_port(mgr, src); + if (src_id == NO_PORT) { + pw_log_error("protocol-jack %p: port_name %s does not exist", impl, src); + goto reply; + } + dst_id = jack_graph_manager_find_port(mgr, dst); + if (dst_id == NO_PORT) { + pw_log_error("protocol-jack %p: port_name %s does not exist", impl, dst); + goto reply; + } + + pw_log_debug("protocol-jack %p: kConnectNamePort %d %s %s %u %u", impl, + ref_num, src, dst, src_id, dst_id); + + src_port = jack_graph_manager_get_port(mgr, src_id); + dst_port = jack_graph_manager_get_port(mgr, dst_id); + + if (((src_port->flags & JackPortIsOutput) == 0) || + ((dst_port->flags & JackPortIsInput) == 0)) { + pw_log_error("protocol-jack %p: ports are not input and output", impl); + goto reply; + } + + if (!src_port->in_use || !dst_port->in_use) { + pw_log_error("protocol-jack %p: ports are not in use", impl); + goto reply; + } + if (src_port->type_id != dst_port->type_id) { + pw_log_error("protocol-jack %p: ports are not of the same type", impl); + goto reply; + } + + conn = jack_graph_manager_next_start(mgr); + + out_ref = jack_connection_manager_get_output_refnum(conn, src_id); + if (out_ref == -1) { + pw_log_error("protocol-jack %p: unknown port_id %d", impl, src_id); + goto reply_stop; + } + if ((jc = server->client_table[out_ref]) == NULL) { + pw_log_error("protocol-jack %p: unknown client %d", impl, out_ref); + goto reply_stop; + } + if (!jc->control->active) { + pw_log_error("protocol-jack %p: can't connect ports of inactive client", impl); + goto reply_stop; + } + out_port = pw_jack_node_find_port(jc->node, PW_DIRECTION_OUTPUT, src_id); + + in_ref = jack_connection_manager_get_input_refnum(conn, dst_id); + if (in_ref == -1) { + pw_log_error("protocol-jack %p: unknown port_id %d", impl, dst_id); + goto reply_stop; + } + if ((jc = server->client_table[in_ref]) == NULL) { + pw_log_error("protocol-jack %p: unknown client %d", impl, in_ref); + goto reply_stop; + } + if (!jc->control->active) { + pw_log_error("protocol-jack %p: can't connect ports of inactive client", impl); + goto reply_stop; + } + in_port = pw_jack_node_find_port(jc->node, PW_DIRECTION_INPUT, dst_id); + + if (jack_connection_manager_is_connected(conn, src_id, dst_id)) { + pw_log_error("protocol-jack %p: ports are already connected", impl); + goto reply_stop; + } + if (jack_connection_manager_connect(conn, src_id, dst_id) < 0) { + pw_log_error("protocol-jack %p: connection table is full", impl); + goto reply_stop; + } + if (jack_connection_manager_connect(conn, dst_id, src_id) < 0) { + pw_log_error("protocol-jack %p: connection table is full", impl); + goto reply_stop; + } + if (jack_connection_manager_is_loop_path(conn, src_id, dst_id) < 0) + jack_connection_manager_inc_feedback_connection(conn, src_id, dst_id); + else + jack_connection_manager_inc_direct_connection(conn, src_id, dst_id); + + pw_log_debug("%p %p", out_port, in_port); + + link = pw_link_new(impl->core, + pw_module_get_global(impl->module), + out_port, + in_port, + NULL, + NULL, + NULL, + 0); + pw_link_activate(link); + + result = 0; + reply_stop: + jack_graph_manager_next_stop(mgr); + + reply: CheckWrite(&result, sizeof(int)); return 0; } @@ -417,8 +698,7 @@ client_busy_changed(void *data, bool busy) } static void -connection_data(struct spa_loop_utils *utils, - struct spa_source *source, int fd, enum spa_io mask, void *data) +connection_data(void *data, int fd, enum spa_io mask) { struct client *client = data; @@ -473,7 +753,7 @@ static struct client *client_new(struct impl *impl, int fd) pw_client_add_listener(client, &this->client_listener, &client_events, this); - pw_log_error("module-jack %p: added new client", impl); + pw_log_debug("module-jack %p: added new client", impl); return this; @@ -484,9 +764,10 @@ static struct client *client_new(struct impl *impl, int fd) } static int -make_int_client(struct impl *impl, struct pw_node *node) +make_audio_client(struct impl *impl) { struct jack_server *server = &impl->server; + struct jack_graph_manager *mgr = server->graph_manager; struct jack_connection_manager *conn; int ref_num; struct jack_client *jc; @@ -507,7 +788,6 @@ make_int_client(struct impl *impl, struct pw_node *node) jc = calloc(1,sizeof(struct jack_client)); jc->ref_num = ref_num; - jc->node = node; jc->control = jack_client_control_alloc("system", -1, ref_num, -1); jc->control->active = true; @@ -515,26 +795,89 @@ make_int_client(struct impl *impl, struct pw_node *node) impl->server.engine_control->driver_num++; - conn = jack_graph_manager_next_start(server->graph_manager); + conn = jack_graph_manager_next_start(mgr); jack_connection_manager_init_ref_num(conn, ref_num); + jack_connection_manager_direct_connect(conn, ref_num, ref_num); - port_id = jack_graph_manager_allocate_port(server->graph_manager, - ref_num, "system:playback_1", 2, - JackPortIsInput | JackPortIsPhysical | JackPortIsTerminal); - + port_id = jack_graph_manager_allocate_port(mgr, + ref_num, "system:playback_1", 0, + JackPortIsInput | + JackPortIsPhysical | + JackPortIsTerminal); jack_connection_manager_add_port(conn, true, ref_num, port_id); - jack_graph_manager_next_stop(server->graph_manager); + port_id = jack_graph_manager_allocate_port(mgr, + ref_num, "system:playback_2", 0, + JackPortIsInput | + JackPortIsPhysical | + JackPortIsTerminal); + jack_connection_manager_add_port(conn, true, ref_num, port_id); + + jack_graph_manager_next_stop(mgr); + + server->audio_ref_num = ref_num; + + server->audio_node = pw_jack_node_new(impl->core, pw_module_get_global(impl->module), + server, ref_num, NULL); + server->audio_node_node = pw_jack_node_get_node(server->audio_node); + jc->node = server->audio_node; + + pw_log_debug("module-jack %p: Added audio driver %d", impl, ref_num); + + return 0; +} + +static int +make_freewheel_client(struct impl *impl) +{ + struct jack_server *server = &impl->server; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_connection_manager *conn; + int ref_num; + struct jack_client *jc; + + ref_num = jack_server_allocate_ref_num(server); + if (ref_num == -1) + return -1; + + if (jack_synchro_init(&server->synchro_table[ref_num], + "freewheel", + server->engine_control->server_name, + 0, + false, + server->promiscuous) < 0) { + return -1; + } + + jc = calloc(1,sizeof(struct jack_client)); + jc->ref_num = ref_num; + jc->control = jack_client_control_alloc("freewheel", -1, ref_num, -1); + jc->control->active = true; + + server->client_table[ref_num] = jc; + + impl->server.engine_control->driver_num++; + + conn = jack_graph_manager_next_start(mgr); + + jack_connection_manager_init_ref_num(conn, ref_num); + jack_connection_manager_direct_connect(conn, ref_num, ref_num); + + jack_graph_manager_next_stop(mgr); + + server->freewheel_ref_num = ref_num; + pw_log_debug("module-jack %p: Added freewheel driver %d", impl, ref_num); return 0; } static bool on_global(void *data, struct pw_global *global) + { struct impl *impl = data; - struct pw_node *node; - struct pw_properties *properties; + struct pw_node *node, *n; + const struct pw_properties *properties; const char *str; if (pw_global_get_type(global) != impl->t->node) @@ -549,16 +892,78 @@ static bool on_global(void *data, struct pw_global *global) if (strcmp(str, "Audio/Sink") != 0) return true; - make_int_client(impl, node); - return true; + n = pw_jack_node_get_node(impl->server.audio_node); + + impl->sink_link = pw_link_new(impl->core, pw_module_get_global(impl->module), + pw_node_get_free_port(n, PW_DIRECTION_OUTPUT), + pw_node_get_free_port(node, PW_DIRECTION_INPUT), + NULL, + NULL, + NULL, + 0); + pw_link_inc_idle(impl->sink_link); + + return false; } +#if 0 +static void on_timeout(void *data, uint64_t expirations) +{ + struct impl *impl = data; + struct jack_server *server = &impl->server; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_connection_manager *conn; + struct jack_client *client; + int activation; + + client = server->client_table[server->freewheel_ref_num]; + + conn = jack_graph_manager_try_switch(mgr); + + jack_connection_manager_reset(conn, mgr->client_timing); + + activation = jack_connection_manager_get_activation(conn, server->freewheel_ref_num); + if (activation == 0) + return; + + pw_log_trace("resume %d", activation); + jack_connection_manager_resume_ref_num(conn, + client->control, + server->synchro_table, + mgr->client_timing); + + if (server->engine_control->sync_mode) { + pw_log_trace("suspend"); + jack_connection_manager_suspend_ref_num(conn, + client->control, + server->synchro_table, + mgr->client_timing); + } +} +#endif static bool init_nodes(struct impl *impl) { struct pw_core *core = impl->core; +#if 0 + struct timespec timeout, interval; +#endif + + make_audio_client(impl); + make_freewheel_client(impl); + +#if 0 + timeout.tv_sec = 0; + timeout.tv_nsec = 1; + interval.tv_sec = 0; + interval.tv_nsec = 10 * SPA_NSEC_PER_MSEC; + + impl->timer = pw_loop_add_timer(pw_core_get_main_loop(impl->core), on_timeout, impl); + pw_loop_update_timer(pw_core_get_main_loop(impl->core), impl->timer, &timeout, &interval, false); +#endif pw_core_for_each_global(core, on_global, impl); + return true; } @@ -586,36 +991,8 @@ static void destroy_socket(struct socket *s) free(s); } -static bool init_socket_name(struct socket *s, const char *name, bool promiscuous, int which) -{ - int name_size; - const char *runtime_dir; - - runtime_dir = JACK_SOCKET_DIR; - - s->addr.sun_family = AF_UNIX; - if (promiscuous) { - name_size = snprintf(s->addr.sun_path, sizeof(s->addr.sun_path), - "%s/jack_%s_%d", runtime_dir, name, which) + 1; - } else { - name_size = snprintf(s->addr.sun_path, sizeof(s->addr.sun_path), - "%s/jack_%s_%d_%d", runtime_dir, name, getuid(), which) + 1; - } - - s->core_name = (s->addr.sun_path + name_size - 1) - strlen(name); - - if (name_size > (int) sizeof(s->addr.sun_path)) { - pw_log_error("socket path \"%s/%s\" plus null terminator exceeds 108 bytes", - runtime_dir, name); - *s->addr.sun_path = 0; - return false; - } - return true; -} - static void -socket_data(struct spa_loop_utils *utils, - struct spa_source *source, int fd, enum spa_io mask, void *data) +socket_data(void *data, int fd, enum spa_io mask) { struct impl *impl = data; struct client *client; @@ -682,6 +1059,8 @@ static int init_server(struct impl *impl, const char *name, bool promiscuous) jack_cleanup_shm(); + server->promiscuous = promiscuous; + /* graph manager */ server->graph_manager = jack_graph_manager_alloc(2048); @@ -696,7 +1075,7 @@ static int init_server(struct impl *impl, const char *name, bool promiscuous) s = create_socket(); - if (!init_socket_name(s, name, promiscuous, 0)) + if (!init_socket_name(&s->addr, name, promiscuous, 0)) goto error; if (!add_socket(impl, s)) diff --git a/src/modules/module-jack/defs.h b/src/modules/module-jack/defs.h index 5ecb9724b..a362b48d6 100644 --- a/src/modules/module-jack/defs.h +++ b/src/modules/module-jack/defs.h @@ -30,6 +30,7 @@ #define JACK_PORT_NAME_SIZE 256 #define JACK_PORT_TYPE_SIZE 32 #define JACK_PROTOCOL_VERSION 8 +#define JACK_MESSAGE_SIZE 256 #define PORT_NUM_MAX 4096 #define PORT_NUM_FOR_CLIENT 2048 @@ -60,8 +61,6 @@ #define EMPTY 0xFFFD #define FREE 0xFFFC - - typedef enum { JACK_TIMER_SYSTEM_CLOCK, JACK_TIMER_HPET, diff --git a/src/modules/module-jack/jack-node.c b/src/modules/module-jack/jack-node.c new file mode 100644 index 000000000..a5c4e0166 --- /dev/null +++ b/src/modules/module-jack/jack-node.c @@ -0,0 +1,508 @@ +/* PipeWire + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "pipewire/pipewire.h" +#include "pipewire/core.h" +#include "pipewire/private.h" + +#include "jack.h" +#include "jack-node.h" + +#define NAME "jack-node" + +/** \cond */ + +struct type { + uint32_t format; + struct spa_type_data data; + struct spa_type_media_type media_type; + struct spa_type_media_subtype media_subtype; + struct spa_type_format_audio format_audio; + struct spa_type_audio_format audio_format; +}; + +struct pw_jack_node { + struct pw_core *core; + struct pw_node *node; + + struct spa_hook_list listener_list; + + struct jack_server *server; + struct type type; + int ref_num; + struct pw_port *otherport; +}; + +static inline void init_type(struct type *type, struct spa_type_map *map) +{ + type->format = spa_type_map_get_id(map, SPA_TYPE__Format); + spa_type_data_map(map, &type->data); + spa_type_media_type_map(map, &type->media_type); + spa_type_media_subtype_map(map, &type->media_subtype); + spa_type_format_audio_map(map, &type->format_audio); + spa_type_audio_format_map(map, &type->audio_format); +} + +struct buffer { + struct spa_list link; + struct spa_buffer *outbuf; + void *ptr; + size_t size; +}; + +struct port_data { + struct pw_jack_node *node; + + int jack_port_id; + struct jack_port *port; + + struct spa_port_info info; + + struct spa_port_io *io; + + struct buffer buffers[64]; + uint32_t n_buffers; + struct spa_list empty; + + uint8_t buffer[1024]; +}; + +/** \endcond */ + +static int node_get_props(void *data, struct spa_props **props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static int node_set_props(void *data, const struct spa_props *props) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static int node_send_command(void *data, + const struct spa_command *command) +{ + return SPA_RESULT_OK; +} + +static struct pw_port* node_add_port(void *data, + enum pw_direction direction, + uint32_t port_id) +{ + return NULL; +} + +static struct buffer *buffer_dequeue(struct pw_jack_node *this, struct port_data *pd) +{ + struct buffer *b; + + if (spa_list_is_empty(&pd->empty)) + return NULL; + + b = spa_list_first(&pd->empty, struct buffer, link); + spa_list_remove(&b->link); + + return b; +} + +static void recycle_buffer(struct pw_jack_node *this, struct port_data *pd, uint32_t id) +{ + struct buffer *b = &pd->buffers[id]; + pw_log_trace("recycle buffer %d", id); + spa_list_append(&pd->empty, &b->link); +} + +static int node_process_input(void *data) +{ + struct pw_jack_node *this = data; + struct spa_graph_node *node = &this->node->rt.node; + struct spa_graph_port *p; + struct buffer *out; + struct port_data *opd = pw_port_get_user_data(this->otherport); + struct spa_port_io *out_io = opd->io; + + pw_log_trace("process input"); + if (out_io->status == SPA_RESULT_HAVE_BUFFER) + return SPA_RESULT_HAVE_BUFFER; + + out = buffer_dequeue(this, opd); + if (out == NULL) + return SPA_RESULT_OUT_OF_BUFFERS; + + spa_list_for_each(p, &node->ports[SPA_DIRECTION_INPUT], link) { + struct pw_port *port = p->callbacks_data; + struct port_data *ipd = pw_port_get_user_data(port); + struct spa_port_io *in_io = ipd->io; + + in_io->status = SPA_RESULT_NEED_BUFFER; + } + + out_io->buffer_id = out->outbuf->id; + out_io->status = SPA_RESULT_HAVE_BUFFER; + + return SPA_RESULT_HAVE_BUFFER; +} + +static int node_process_output(void *data) +{ + struct pw_jack_node *this = data; + struct spa_graph_node *node = &this->node->rt.node; + struct spa_graph_port *p; + + pw_log_trace(NAME "%p: process output", this); + + spa_list_for_each(p, &node->ports[SPA_DIRECTION_OUTPUT], link) { + struct pw_port *port = p->callbacks_data; + struct port_data *opd = pw_port_get_user_data(port); + struct spa_port_io *out_io = opd->io; + + if (out_io->status == SPA_RESULT_HAVE_BUFFER) + return SPA_RESULT_HAVE_BUFFER; + + if (out_io->buffer_id != SPA_ID_INVALID) { + recycle_buffer(this, opd, out_io->buffer_id); + out_io->buffer_id = SPA_ID_INVALID; + } + } + + spa_list_for_each(p, &node->ports[SPA_DIRECTION_INPUT], link) { + struct pw_port *port = p->callbacks_data; + struct port_data *ipd = pw_port_get_user_data(port); + struct spa_port_io *in_io = ipd->io; + + in_io->status = SPA_RESULT_NEED_BUFFER; + } + + spa_hook_list_call(&this->listener_list, struct pw_jack_node_events, process); + + return SPA_RESULT_NEED_BUFFER; +} + +static const struct pw_node_implementation node_impl = { + PW_VERSION_NODE_IMPLEMENTATION, + .get_props = node_get_props, + .set_props = node_set_props, + .send_command = node_send_command, + .add_port = node_add_port, + .process_input = node_process_input, + .process_output = node_process_output, +}; + +static int port_set_io(void *data, struct spa_port_io *io) +{ + struct port_data *pd = data; + pd->io = io; + return SPA_RESULT_OK; +} + +#define PROP(f,key,type,...) \ + SPA_POD_PROP (f,key,0,type,1,__VA_ARGS__) + +static int port_enum_formats(void *data, + struct spa_format **format, + const struct spa_format *filter, + int32_t index) +{ + struct port_data *pd = data; + struct type *t = &pd->node->type; + struct spa_pod_builder b = { NULL, }; + struct spa_pod_frame f[2]; + + if (index > 0) + return SPA_RESULT_ENUM_END; + + spa_pod_builder_init(&b, pd->buffer, sizeof(pd->buffer)); + + if (pd->port) { + if (pd->port->type_id == 0) { + spa_pod_builder_format(&b, &f[0], t->format, + t->media_type.audio, t->media_subtype.raw, + PROP(&f[1], t->format_audio.format, SPA_POD_TYPE_ID, t->audio_format.F32), + PROP(&f[1], t->format_audio.rate, SPA_POD_TYPE_INT, 44100), + PROP(&f[1], t->format_audio.channels, SPA_POD_TYPE_INT, 1)); + } + else if (pd->port->type_id == 1) { + return SPA_RESULT_ENUM_END; + } + else + return SPA_RESULT_ENUM_END; + } + else { + spa_pod_builder_format(&b, &f[0], t->format, + t->media_type.audio, t->media_subtype.raw, + PROP(&f[1], t->format_audio.format, SPA_POD_TYPE_ID, t->audio_format.S16), + PROP(&f[1], t->format_audio.rate, SPA_POD_TYPE_INT, 44100), + PROP(&f[1], t->format_audio.channels, SPA_POD_TYPE_INT, 2)); + } + *format = SPA_POD_BUILDER_DEREF(&b, f[0].ref, struct spa_format); + + return SPA_RESULT_OK; +} + +static int port_set_format(void *data, uint32_t flags, const struct spa_format *format) +{ + return SPA_RESULT_OK; +} + +static int port_get_format(void *data, const struct spa_format **format) +{ + return SPA_RESULT_OK; +} + +static int port_get_info(void *data, const struct spa_port_info **info) +{ + struct port_data *pd = data; + + pd->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS | SPA_PORT_INFO_FLAG_LIVE; + pd->info.rate = 44100; + *info = &pd->info; + + return SPA_RESULT_OK; +} + +static int port_enum_params(void *data, uint32_t index, struct spa_param **param) +{ + return SPA_RESULT_ENUM_END; +} + +static int port_set_param(void *data, struct spa_param *param) +{ + return SPA_RESULT_OK; +} + +static int port_use_buffers(void *data, struct spa_buffer **buffers, uint32_t n_buffers) +{ + struct port_data *pd = data; + struct type *t = &pd->node->type; + int i; + + for (i = 0; i < n_buffers; i++) { + struct buffer *b; + struct spa_data *d = buffers[i]->datas; + + b = &pd->buffers[i]; + b->outbuf = buffers[i]; + if ((d[0].type == t->data.MemPtr || + d[0].type == t->data.MemFd || + d[0].type == t->data.DmaBuf) && d[0].data != NULL) { + b->ptr = d[0].data; + b->size = d[0].maxsize; + } else { + pw_log_error(NAME " %p: invalid memory on buffer %p", pd, buffers[i]); + return SPA_RESULT_ERROR; + } + spa_list_append(&pd->empty, &b->link); + } + pd->n_buffers = n_buffers; + + return SPA_RESULT_OK; +} + +static int port_alloc_buffers(void *data, + struct spa_param **params, uint32_t n_params, + struct spa_buffer **buffers, uint32_t *n_buffers) +{ + return SPA_RESULT_NOT_IMPLEMENTED; +} + +static int port_reuse_buffer(void *data, uint32_t buffer_id) +{ + return SPA_RESULT_OK; +} + +static int port_send_command(void *data, struct spa_command *command) +{ + return SPA_RESULT_OK; +} + +static const struct pw_port_implementation port_impl = { + PW_VERSION_PORT_IMPLEMENTATION, + .set_io = port_set_io, + .enum_formats = port_enum_formats, + .set_format = port_set_format, + .get_format = port_get_format, + .get_info = port_get_info, + .enum_params = port_enum_params, + .set_param = port_set_param, + .use_buffers = port_use_buffers, + .alloc_buffers = port_alloc_buffers, + .reuse_buffer = port_reuse_buffer, + .send_command = port_send_command, +}; + +static struct pw_port *make_port(struct pw_jack_node *node, enum pw_direction direction, + int port_id, int jack_port_id, struct jack_port *jp, bool autoconnect) +{ + struct pw_port *port; + struct port_data *pd; + struct pw_properties *properties = NULL; + + if (autoconnect) + properties = pw_properties_new("pipewire.autoconnect", "1", NULL); + + port = pw_port_new(direction, port_id, properties, sizeof(struct port_data)); + pd = pw_port_get_user_data(port); + pd->node = node; + pd->jack_port_id = jack_port_id; + pd->port = jp; + spa_list_init(&pd->empty); + pw_port_set_implementation(port, &port_impl, pd); + pw_port_add(port, node->node); + + return port; +} + +struct pw_jack_node *pw_jack_node_new(struct pw_core *core, + struct pw_global *parent, + struct jack_server *server, + int ref_num, + struct pw_properties *properties) +{ + struct pw_jack_node *this; + struct pw_node *node; + struct jack_client *client = server->client_table[ref_num]; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_connection_manager *conn; + int i; + jack_int_t *p; + bool make_input = false, make_output = false; + + node = pw_node_new(core, NULL, parent, client->control->name, + properties, sizeof(struct pw_jack_node)); + if (node == NULL) + return NULL; + + this = pw_node_get_user_data(node); + pw_log_debug("jack-node %p: new", this); + + this->node = node; + this->core = core; + spa_hook_list_init(&this->listener_list); + this->server = server; + this->ref_num = ref_num; + init_type(&this->type, pw_core_get_type(core)->map); + + pw_node_set_implementation(node, &node_impl, this); + + conn = jack_graph_manager_next_start(mgr); + + p = GET_ITEMS_FIXED_ARRAY1(conn->input_port[ref_num]); + for (i = 0; i < PORT_NUM_FOR_CLIENT && p[i] != EMPTY; i++) { + struct jack_port *jp = jack_graph_manager_get_port(mgr, p[i]); + + if (jp->flags & JackPortIsPhysical) + make_output = true; + + make_port(this, PW_DIRECTION_INPUT, i, p[i], jp, false); + } + + p = GET_ITEMS_FIXED_ARRAY(conn->output_port[ref_num]); + for (i = 0; i < PORT_NUM_FOR_CLIENT && p[i] != EMPTY; i++) { + struct jack_port *jp = jack_graph_manager_get_port(mgr, p[i]); + + if (jp->flags & JackPortIsPhysical) + make_input = true; + + make_port(this, PW_DIRECTION_OUTPUT, i, p[i], jp, false); + } + jack_graph_manager_next_stop(mgr); + + if (make_output) + this->otherport = make_port(this, PW_DIRECTION_OUTPUT, 0, -1, NULL, true); + if (make_input) + this->otherport = make_port(this, PW_DIRECTION_INPUT, 0, -1, NULL, true); + + pw_node_register(node); + + return this; +} + +void pw_jack_node_destroy(struct pw_jack_node *node) +{ + pw_log_debug("jack-node %p: destroy", node); + pw_node_destroy(node->node); + free(node); +} + +struct pw_node *pw_jack_node_get_node(struct pw_jack_node *node) +{ + return node->node; +} + +void pw_jack_node_add_listener(struct pw_jack_node *node, + struct spa_hook *listener, + const struct pw_jack_node_events *events, + void *data) +{ + spa_hook_list_append(&node->listener_list, listener, events, data); +} + +struct find_data { + jack_port_id_t port_id; + struct pw_port *result; +}; + +static bool find_port(void *data, struct pw_port *port) +{ + struct find_data *d = data; + struct port_data *pd = pw_port_get_user_data(port); + + if (pd->jack_port_id == d->port_id) { + d->result = port; + return false; + } + return true; +} + +struct pw_port *pw_jack_node_add_port(struct pw_jack_node *node, + enum pw_direction direction, + jack_port_id_t port_id) +{ + struct jack_server *server = node->server; + struct jack_graph_manager *mgr = server->graph_manager; + struct jack_port *jp = jack_graph_manager_get_port(mgr, port_id); + return make_port(node, direction, port_id, port_id, jp, false); +} + +struct pw_port *pw_jack_node_find_port(struct pw_jack_node *node, + enum pw_direction direction, + jack_port_id_t port_id) +{ + struct find_data data = { port_id, }; + if (!pw_node_for_each_port(node->node, direction, find_port, &data)) + return data.result; + return NULL; +} diff --git a/src/modules/module-jack/jack-node.h b/src/modules/module-jack/jack-node.h new file mode 100644 index 000000000..185e2ec47 --- /dev/null +++ b/src/modules/module-jack/jack-node.h @@ -0,0 +1,73 @@ +/* PipeWire + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PIPEWIRE_JACK_NODE_H__ +#define __PIPEWIRE_JACK_NODE_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \class pw_jack_node + * + * PipeWire jack node interface + */ +struct pw_jack_node; + +struct pw_jack_node_events { +#define PW_VERSION_JACK_NODE_EVENTS 0 + uint32_t version; + + void (*destroy) (void *data); + + void (*process) (void *data); +}; + + +struct pw_jack_node * +pw_jack_node_new(struct pw_core *core, + struct pw_global *parent, + struct jack_server *server, + int ref_num, + struct pw_properties *properties); + +void +pw_jack_node_destroy(struct pw_jack_node *node); + +struct pw_node *pw_jack_node_get_node(struct pw_jack_node *node); + +void pw_jack_node_add_listener(struct pw_jack_node *node, + struct spa_hook *listener, + const struct pw_jack_node_events *events, + void *data); + +struct pw_port *pw_jack_node_add_port(struct pw_jack_node *node, + enum pw_direction direction, + jack_port_id_t port_id); + +struct pw_port *pw_jack_node_find_port(struct pw_jack_node *node, + enum pw_direction direction, jack_port_id_t port_id); + +#ifdef __cplusplus +} +#endif + +#endif /* __PIPEWIRE_JACK_NODE_H__ */ diff --git a/src/modules/module-jack/jack.h b/src/modules/module-jack/jack.h new file mode 100644 index 000000000..6fc721937 --- /dev/null +++ b/src/modules/module-jack/jack.h @@ -0,0 +1,28 @@ +/* PipeWire + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "modules/module-jack/defs.h" +#include "modules/module-jack/synchro.h" +#include "modules/module-jack/shm.h" +#include "modules/module-jack/shared.h" +#include "modules/module-jack/port.h" +#include "modules/module-jack/server.h" diff --git a/src/modules/module-jack/port.h b/src/modules/module-jack/port.h new file mode 100644 index 000000000..064b618a5 --- /dev/null +++ b/src/modules/module-jack/port.h @@ -0,0 +1,28 @@ +/* PipeWire + * Copyright (C) 2017 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +static inline jack_port_type_id_t +jack_port_get_type_id(const char *type) +{ + if (strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) + return 0; + else if (strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) + return 1; + return 2; +} diff --git a/src/modules/module-jack/server.h b/src/modules/module-jack/server.h index c5f6c0c9d..27313cde3 100644 --- a/src/modules/module-jack/server.h +++ b/src/modules/module-jack/server.h @@ -21,7 +21,8 @@ struct jack_client { int ref_num; struct client *owner; struct jack_client_control *control; - struct pw_node *node; + struct pw_jack_node *node; + int fd; /* notify fd */ }; struct jack_server { @@ -34,6 +35,13 @@ struct jack_server { struct jack_client* client_table[CLIENT_NUM]; struct jack_synchro synchro_table[CLIENT_NUM]; + + int audio_ref_num; + int freewheel_ref_num; + + struct pw_jack_node *audio_node; + struct pw_node *audio_node_node; + int audio_used; }; static inline int diff --git a/src/modules/module-jack/shared.h b/src/modules/module-jack/shared.h index 2d4cfaf83..a510e7de8 100644 --- a/src/modules/module-jack/shared.h +++ b/src/modules/module-jack/shared.h @@ -98,6 +98,64 @@ static inline void jack_port_init(struct jack_port *port, int ref_num, port->tied = NO_PORT; } +static inline void jack_port_release(struct jack_port *port) { + port->in_use = false; +} + +PRE_PACKED_STRUCTURE +struct jack_client_control { + jack_shm_info_t info; + char name[JACK_CLIENT_NAME_SIZE+1]; + bool callback[jack_notify_max]; + volatile jack_transport_state_t transport_state; + volatile bool transport_sync; + volatile bool transport_timebase; + int ref_num; + int PID; + bool active; + + int session_ID; + char session_command[JACK_SESSION_COMMAND_SIZE]; + jack_session_flags_t session_flags; +} POST_PACKED_STRUCTURE; + +static inline struct jack_client_control * +jack_client_control_alloc(const char* name, int pid, int ref_num, int uuid) +{ + struct jack_client_control *ctrl; + jack_shm_info_t info; + size_t size; + + size = sizeof(struct jack_client_control); + if (jack_shm_alloc(size, &info, segment_num++) < 0) + return NULL; + + ctrl = (struct jack_client_control *)jack_shm_addr(&info); + ctrl->info = info; + + strcpy(ctrl->name, name); + for (int i = 0; i < jack_notify_max; i++) + ctrl->callback[i] = false; + + // Always activated + ctrl->callback[jack_notify_AddClient] = true; + ctrl->callback[jack_notify_RemoveClient] = true; + ctrl->callback[jack_notify_ActivateClient] = true; + ctrl->callback[jack_notify_LatencyCallback] = true; + // So that driver synchro are correctly setup in "flush" or "normal" mode + ctrl->callback[jack_notify_StartFreewheelCallback] = true; + ctrl->callback[jack_notify_StopFreewheelCallback] = true; + ctrl->ref_num = ref_num; + ctrl->PID = pid; + ctrl->transport_state = JackTransportStopped; + ctrl->transport_sync = false; + ctrl->transport_timebase = false; + ctrl->active = false; + ctrl->session_ID = uuid; + + return ctrl; +} + #define MAKE_FIXED_ARRAY(size) \ PRE_PACKED_STRUCTURE \ struct { \ @@ -105,24 +163,41 @@ struct { \ uint32_t counter; \ } POST_PACKED_STRUCTURE -#define INIT_FIXED_ARRAY(arr) ({ \ - int i; \ - for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) \ - arr.table[i] = EMPTY; \ - arr.counter = 0; \ +#define INIT_FIXED_ARRAY(arr) ({ \ + int _i; \ + for (_i = 0; _i < SPA_N_ELEMENTS(arr.table); _i++) \ + arr.table[_i] = EMPTY; \ + arr.counter = 0; \ +}) +#define GET_ITEMS_FIXED_ARRAY(arr) ({ \ + arr.table; \ }) - #define ADD_FIXED_ARRAY(arr,item) ({ \ - int i,ret = -1; \ - for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \ - if (arr.table[i] == EMPTY) { \ - arr.table[i] = item; \ - arr.counter++; \ - ret = 0; \ + int _ret = -1; \ + if (arr.counter < SPA_N_ELEMENTS(arr.table)) { \ + _ret = arr.counter++; \ + arr.table[_ret] = item; \ + } \ + _ret; \ +}) +#define GET_FIXED_ARRAY(arr,item) ({ \ + int _i,_ret = -1; \ + for (_i = 0; _i < arr.counter; _i++) { \ + if (arr.table[_i] == item) { \ + _ret = _i; \ break; \ } \ } \ - ret; \ + _ret; \ +}) +#define REMOVE_FIXED_ARRAY(arr,item) ({ \ + int _ret = GET_FIXED_ARRAY(arr,item); \ + if (_ret >= 0) { \ + arr.counter--; \ + arr.table[_ret] = arr.table[arr.counter]; \ + arr.table[arr.counter] = EMPTY; \ + } \ + _ret; \ }) #define MAKE_FIXED_ARRAY1(size) \ @@ -136,8 +211,9 @@ struct { \ INIT_FIXED_ARRAY(arr.array); \ arr.used = false; \ }) - #define ADD_FIXED_ARRAY1(arr,item) ADD_FIXED_ARRAY(arr.array,item) +#define GET_FIXED_ARRAY1(arr,item) GET_FIXED_ARRAY(arr.array,item) +#define GET_ITEMS_FIXED_ARRAY1(arr) GET_ITEMS_FIXED_ARRAY(arr.array) #define MAKE_FIXED_MATRIX(size) \ PRE_PACKED_STRUCTURE \ @@ -152,6 +228,21 @@ struct { \ mat.table[i][idx] = 0; \ } \ }) +#define GET_ITEMS_FIXED_MATRIX(mat,idx1) ({ \ + mat.table[idx1]; \ +}) +#define INC_FIXED_MATRIX(mat,idx1,idx2) ({ \ + ++mat.table[idx1][idx2]; \ +}) +#define DEC_FIXED_MATRIX(mat,idx1,idx2) ({ \ + --mat.table[idx1][idx2]; \ +}) +#define GET_FIXED_MATRIX(mat,idx1,idx2) ({ \ + mat.table[idx1][idx2]; \ +}) +#define CLEAR_FIXED_MATRIX(mat,idx1,idx2) ({ \ + mat.table[idx1][idx2] = 0; \ +}) PRE_PACKED_STRUCTURE struct jack_activation_count { @@ -162,6 +253,33 @@ struct jack_activation_count { static inline void jack_activation_count_set_value(struct jack_activation_count *cnt, int32_t val) { cnt->value = val; } +static inline int32_t jack_activation_count_get_value(struct jack_activation_count *cnt) { + return cnt->value; +} +static inline int32_t jack_activation_count_get_count(struct jack_activation_count *cnt) { + return cnt->count; +} +static inline void jack_activation_count_reset(struct jack_activation_count *cnt) { + cnt->value = cnt->count; +} +static inline void jack_activation_count_inc_value(struct jack_activation_count *cnt) { + cnt->count++; +} +static inline void jack_activation_count_dec_value(struct jack_activation_count *cnt) { + cnt->count--; +} +static inline bool jack_activation_count_signal(struct jack_activation_count *cnt, + struct jack_synchro *synchro) +{ + bool res = true; + + if (cnt->value == 0) + res = jack_synchro_signal(synchro); + else if (__atomic_sub_fetch(&cnt->value, 1, __ATOMIC_SEQ_CST) == 0) + res = jack_synchro_signal(synchro); + + return res; +} #define MAKE_LOOP_FEEDBACK(size) \ PRE_PACKED_STRUCTURE \ @@ -177,6 +295,62 @@ struct { \ arr.table[i][2] = 0; \ } \ }) +#define ADD_LOOP_FEEDBACK(arr,ref1,ref2) ({ \ + int i,res = false; \ + for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \ + if (arr.table[i][0] == EMPTY) { \ + arr.table[i][0] = ref1; \ + arr.table[i][1] = ref2; \ + arr.table[i][2] = 1; \ + res = true; \ + break; \ + } \ + } \ + res; \ +}) +#define DEL_LOOP_FEEDBACK(arr,ref1,ref2) ({ \ + int i,res = false; \ + for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \ + if (arr.table[i][0] == ref1 && \ + arr.table[i][1] == ref2) { \ + arr.table[i][0] = EMPTY; \ + arr.table[i][1] = EMPTY; \ + arr.table[i][2] = 0; \ + res = true; \ + break; \ + } \ + } \ + res; \ +}) +#define GET_LOOP_FEEDBACK(arr,ref1,ref2) ({ \ + int i,res = 1; \ + for (i = 0; i < SPA_N_ELEMENTS(arr.table); i++) { \ + if (arr.table[i][0] == ref1 && \ + arr.table[i][1] == ref2) { \ + res = i; \ + break; \ + } \ + } \ + res; \ +}) +#define INC_LOOP_FEEDBACK(arr,ref1,ref2) ({ \ + int res = true, idx = GET_LOOP_FEEDBACK(arr,ref1,ref2); \ + if (idx >= 0) \ + arr.table[idx][2]++; \ + else \ + res = ADD_LOOP_FEEDBACK(arr,ref1,ref2); \ + res; \ +}) +#define DEC_LOOP_FEEDBACK(arr,idx) ({ \ + int res = true, idx = GET_LOOP_FEEDBACK(arr,ref1,ref2); \ + if (idx >= 0) { \ + if (--arr.table[idx][2] == 0) \ + res = DEL_LOOP_FEEDBACK(arr,ref1,ref2); \ + } \ + else \ + res = false; \ + res; \ +}) PRE_PACKED_STRUCTURE struct jack_connection_manager { @@ -194,7 +368,8 @@ jack_connection_manager_init_ref_num(struct jack_connection_manager *conn, int r INIT_FIXED_ARRAY1(conn->input_port[ref_num]); INIT_FIXED_ARRAY(conn->output_port[ref_num]); INIT_FIXED_MATRIX(conn->connection_ref, ref_num); - jack_activation_count_set_value (&conn->input_counter[ref_num], 0); + conn->input_counter[ref_num].count = 0; + jack_activation_count_set_value(&conn->input_counter[ref_num], 0); } static inline void @@ -210,14 +385,185 @@ jack_connection_manager_init(struct jack_connection_manager *conn) jack_connection_manager_init_ref_num(conn, i); } +static inline void +jack_connection_manager_reset(struct jack_connection_manager *conn, + struct jack_client_timing *timing) +{ + int i; + for (i = 0; i < CLIENT_NUM; i++) { + jack_activation_count_reset(&conn->input_counter[i]); + timing[i].status = NotTriggered; + } +} + static inline int jack_connection_manager_add_port(struct jack_connection_manager *conn, bool input, int ref_num, jack_port_id_t port_id) { - if (input) + if (input) { return ADD_FIXED_ARRAY1(conn->input_port[ref_num], port_id); - else + } + else { return ADD_FIXED_ARRAY(conn->output_port[ref_num], port_id); + } +} + +static inline int +jack_connection_manager_get_output_refnum(struct jack_connection_manager *conn, + jack_port_id_t port_index) +{ + int i; + for (i = 0; i < CLIENT_NUM; i++) { + if (GET_FIXED_ARRAY(conn->output_port[i], port_index) != -1) + return i; + } + return -1; +} + +static inline int +jack_connection_manager_get_input_refnum(struct jack_connection_manager *conn, + jack_port_id_t port_index) +{ + int i; + for (i = 0; i < CLIENT_NUM; i++) { + if (GET_FIXED_ARRAY1(conn->input_port[i], port_index) != -1) + return i; + } + return -1; +} + +static inline bool +jack_connection_manager_is_connected(struct jack_connection_manager *conn, + jack_port_id_t src_id, jack_port_id_t dst_id) +{ + return GET_FIXED_ARRAY(conn->connection[src_id], dst_id) != -1; +} + +static inline int +jack_connection_manager_connect(struct jack_connection_manager *conn, + jack_port_id_t src_id, jack_port_id_t dst_id) +{ + return ADD_FIXED_ARRAY(conn->connection[src_id], dst_id); +} + +static inline int +jack_connection_manager_disconnect(struct jack_connection_manager *conn, + jack_port_id_t src_id, jack_port_id_t dst_id) +{ + return REMOVE_FIXED_ARRAY(conn->connection[src_id], dst_id); +} + +static inline int +jack_connection_manager_is_loop_path(struct jack_connection_manager *conn, + jack_port_id_t src_id, jack_port_id_t dst_id) +{ + /* FIXME */ + return false; +} + +static inline void +jack_connection_manager_direct_connect(struct jack_connection_manager *conn, + int ref1, int ref2) +{ + if (INC_FIXED_MATRIX(conn->connection_ref, ref1, ref2) == 1) + jack_activation_count_inc_value(&conn->input_counter[ref2]); +} + +static inline bool +jack_connection_manager_is_direct_connection(struct jack_connection_manager *conn, + int ref1, int ref2) +{ + return GET_FIXED_MATRIX(conn->connection_ref, ref1, ref2) > 0; +} + +static inline void +jack_connection_manager_direct_disconnect(struct jack_connection_manager *conn, + int ref1, int ref2) +{ + if (DEC_FIXED_MATRIX(conn->connection_ref, ref1, ref2) == 0) + jack_activation_count_dec_value(&conn->input_counter[ref2]); +} + +static inline bool +jack_connection_manager_inc_feedback_connection(struct jack_connection_manager *conn, + jack_port_id_t src_id, jack_port_id_t dst_id) +{ + int ref1 = jack_connection_manager_get_output_refnum(conn, src_id); + int ref2 = jack_connection_manager_get_input_refnum(conn, dst_id); + + if (ref1 != ref2) + jack_connection_manager_direct_connect(conn, ref2, ref1); + + return INC_LOOP_FEEDBACK(conn->loop_feedback, ref1, ref2); +} + +static inline void +jack_connection_manager_inc_direct_connection(struct jack_connection_manager *conn, + jack_port_id_t src_id, jack_port_id_t dst_id) +{ + int ref1 = jack_connection_manager_get_output_refnum(conn, src_id); + int ref2 = jack_connection_manager_get_input_refnum(conn, dst_id); + + jack_connection_manager_direct_connect(conn, ref1, ref2); +} + +static inline void +jack_connection_manager_dec_direct_connection(struct jack_connection_manager *conn, + jack_port_id_t src_id, jack_port_id_t dst_id) +{ + int ref1 = jack_connection_manager_get_output_refnum(conn, src_id); + int ref2 = jack_connection_manager_get_input_refnum(conn, dst_id); + + jack_connection_manager_direct_disconnect(conn, ref1, ref2); +} + +static inline int +jack_connection_manager_get_activation(struct jack_connection_manager *conn, int ref_num) +{ + return jack_activation_count_get_value(&conn->input_counter[ref_num]); +} + +static inline int +jack_connection_manager_suspend_ref_num(struct jack_connection_manager *conn, + struct jack_client_control *control, + struct jack_synchro *synchro, + struct jack_client_timing *timing) +{ + int res = 0, ref_num = control->ref_num; + jack_time_t current_date = 0; + + if (jack_synchro_wait(&synchro[ref_num])) { + timing[ref_num].status = Finished; + timing[ref_num].awake_at = current_date; + } + return res ? 0 : -1; +} + + +static inline int +jack_connection_manager_resume_ref_num(struct jack_connection_manager *conn, + struct jack_client_control *control, + struct jack_synchro *synchro, + struct jack_client_timing *timing) +{ + int i, res = 0, ref_num = control->ref_num; + const jack_int_t* output_ref = GET_ITEMS_FIXED_MATRIX(conn->connection_ref, ref_num); + jack_time_t current_date = 0; + + timing[ref_num].status = Finished; + timing[ref_num].finished_at = current_date; + + for (i = 0; i < CLIENT_NUM; i++) { + if (output_ref[i] <= 0) + continue; + + timing[i].status = Triggered; + timing[i].signaled_at = current_date; + + if (!jack_activation_count_signal(&conn->input_counter[i], &synchro[i])) + res = -1; + } + return res; } PRE_PACKED_STRUCTURE @@ -286,6 +632,8 @@ jack_graph_manager_alloc(int port_max) mgr = (struct jack_graph_manager *)jack_shm_addr(&info); mgr->info = info; + Counter(mgr->state.counter) = 0; + mgr->state.call_write_counter = 0; jack_connection_manager_init(&mgr->state.state[0]); jack_connection_manager_init(&mgr->state.state[1]); @@ -313,6 +661,31 @@ jack_graph_manager_allocate_port(struct jack_graph_manager *mgr, return NO_PORT; } +static inline void +jack_graph_manager_release_port(struct jack_graph_manager *mgr, jack_port_id_t port_id) +{ + jack_port_release(&mgr->port_array[port_id]); +} + +static inline struct jack_port * +jack_graph_manager_get_port(struct jack_graph_manager *mgr, jack_port_id_t port_index) +{ + if (port_index > 0 && port_index < mgr->port_max) + return &mgr->port_array[port_index]; + return NULL; +} + +static inline jack_port_id_t +jack_graph_manager_find_port(struct jack_graph_manager *mgr, const char *name) +{ + int i; + for (i = 0; i < mgr->port_max; i++) { + struct jack_port *port = &mgr->port_array[i]; + if (port->in_use && strcmp(port->name, name) == 0) + return i; + } + return NO_PORT; +} static inline struct jack_connection_manager * jack_graph_manager_next_start(struct jack_graph_manager *manager) @@ -370,6 +743,38 @@ jack_graph_manager_next_stop(struct jack_graph_manager *manager) } } +static inline bool +jack_graph_manager_is_pending_change(struct jack_graph_manager *manager) +{ + return CurIndex(manager->state.counter) != NextIndex(manager->state.counter); +} + +static inline struct jack_connection_manager * +jack_graph_manager_get_current(struct jack_graph_manager *manager) +{ + return &manager->state.state[CurArrayIndex(manager->state.counter)]; +} + +static inline struct jack_connection_manager * +jack_graph_manager_try_switch(struct jack_graph_manager *manager) +{ + struct jack_atomic_counter old_val; + struct jack_atomic_counter new_val; + do { + old_val = manager->state.counter; + new_val = old_val; + CurIndex(new_val) = NextIndex(new_val); + } + while (!__atomic_compare_exchange_n((uint32_t*)&manager->state.counter, + (uint32_t*)&Counter(old_val), + Counter(new_val), + false, + __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST)); + + return &manager->state.state[CurArrayIndex(manager->state.counter)]; +} + typedef enum { TransportCommandNone = 0, TransportCommandStart = 1, @@ -515,7 +920,7 @@ jack_engine_control_alloc(const char* name) ctrl->buffer_size = 512; ctrl->sample_rate = 48000; - ctrl->sync_mode = false; + ctrl->sync_mode = true; ctrl->temporary = false; ctrl->period_usecs = 1000000.f / ctrl->sample_rate * ctrl->buffer_size; ctrl->timeout_usecs = 0; @@ -545,57 +950,3 @@ jack_engine_control_alloc(const char* name) return ctrl; } - -PRE_PACKED_STRUCTURE -struct jack_client_control { - jack_shm_info_t info; - char name[JACK_CLIENT_NAME_SIZE+1]; - bool callback[jack_notify_max]; - volatile jack_transport_state_t transport_state; - volatile bool transport_sync; - volatile bool transport_timebase; - int ref_num; - int PID; - bool active; - - int session_ID; - char session_command[JACK_SESSION_COMMAND_SIZE]; - jack_session_flags_t session_flags; -} POST_PACKED_STRUCTURE; - -static inline struct jack_client_control * -jack_client_control_alloc(const char* name, int pid, int ref_num, int uuid) -{ - struct jack_client_control *ctrl; - jack_shm_info_t info; - size_t size; - - size = sizeof(struct jack_client_control); - if (jack_shm_alloc(size, &info, segment_num++) < 0) - return NULL; - - ctrl = (struct jack_client_control *)jack_shm_addr(&info); - ctrl->info = info; - - strcpy(ctrl->name, name); - for (int i = 0; i < jack_notify_max; i++) - ctrl->callback[i] = false; - - // Always activated - ctrl->callback[jack_notify_AddClient] = true; - ctrl->callback[jack_notify_RemoveClient] = true; - ctrl->callback[jack_notify_ActivateClient] = true; - ctrl->callback[jack_notify_LatencyCallback] = true; - // So that driver synchro are correctly setup in "flush" or "normal" mode - ctrl->callback[jack_notify_StartFreewheelCallback] = true; - ctrl->callback[jack_notify_StopFreewheelCallback] = true; - ctrl->ref_num = ref_num; - ctrl->PID = pid; - ctrl->transport_state = JackTransportStopped; - ctrl->transport_sync = false; - ctrl->transport_timebase = false; - ctrl->active = false; - ctrl->session_ID = uuid; - - return ctrl; -} diff --git a/src/modules/module-jack/synchro.h b/src/modules/module-jack/synchro.h index e58a368de..35dd75d55 100644 --- a/src/modules/module-jack/synchro.h +++ b/src/modules/module-jack/synchro.h @@ -17,6 +17,8 @@ * Boston, MA 02110-1301, USA. */ +#include + struct jack_synchro { char name[SYNC_MAX_NAME_SIZE]; bool flush; @@ -46,3 +48,29 @@ jack_synchro_init(struct jack_synchro *synchro, } return 0; } + +static inline bool +jack_synchro_signal(struct jack_synchro *synchro) +{ + int res; + if (synchro->flush) + return true; + if ((res = sem_post(synchro->semaphore)) < 0) + pw_log_error("semaphore %s post err = %s", synchro->name, strerror(errno)); + + return res == 0; +} + +static inline bool +jack_synchro_wait(struct jack_synchro *synchro) +{ + int res; + while ((res = sem_wait(synchro->semaphore)) < 0) { + if (errno != EINTR) + continue; + + pw_log_error("semaphore %s wait err = %s", synchro->name, strerror(errno)); + break; + } + return res == 0; +} diff --git a/src/modules/module-mixer.c b/src/modules/module-mixer.c index e3e30f646..e15b24087 100644 --- a/src/modules/module-mixer.c +++ b/src/modules/module-mixer.c @@ -134,7 +134,7 @@ static bool on_global(void *data, struct pw_global *global) { struct impl *impl = data; struct pw_node *n, *node; - struct pw_properties *properties; + const struct pw_properties *properties; const char *str; char *error; struct pw_port *ip, *op; @@ -160,7 +160,7 @@ static bool on_global(void *data, struct pw_global *global) if (op == NULL) return true; - link = pw_link_new(impl->core, pw_module_get_global(impl->module), op, ip, NULL, NULL, &error); + link = pw_link_new(impl->core, pw_module_get_global(impl->module), op, ip, NULL, NULL, &error, 0); pw_link_inc_idle(link); return true; diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index d0076ab6e..cd2ab5ddd 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -190,8 +190,7 @@ client_busy_changed(void *data, bool busy) } static void -connection_data(struct spa_loop_utils *utils, - struct spa_source *source, int fd, enum spa_io mask, void *data) +connection_data(void *data, int fd, enum spa_io mask) { struct client_data *this = data; struct pw_client *client = this->client; @@ -343,8 +342,7 @@ static bool lock_socket(struct listener *l) } static void -socket_data(struct spa_loop_utils *utils, - struct spa_source *source, int fd, enum spa_io mask, void *data) +socket_data(void *data, int fd, enum spa_io mask) { struct listener *l = data; struct pw_client *client; @@ -454,8 +452,7 @@ static int impl_connect(struct pw_protocol_connection *conn) static void -on_remote_data(struct spa_loop_utils *utils, - struct spa_source *source, int fd, enum spa_io mask, void *data) +on_remote_data(void *data, int fd, enum spa_io mask) { struct connection *impl = data; struct pw_remote *this = impl->this.remote; @@ -523,8 +520,7 @@ on_remote_data(struct spa_loop_utils *utils, } -static void do_flush_event(struct spa_loop_utils *utils, struct spa_source *source, - uint64_t count, void *data) +static void do_flush_event(void *data, uint64_t count) { struct connection *impl = data; impl->flush_signaled = false; diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c index 0147c6873..11e08ea46 100644 --- a/src/modules/module-suspend-on-idle.c +++ b/src/modules/module-suspend-on-idle.c @@ -73,7 +73,7 @@ static void node_info_free(struct node_info *info) free(info); } -static void idle_timeout(struct spa_loop_utils *utils, struct spa_source *source, void *data) +static void idle_timeout(void *data, uint64_t expirations) { struct node_info *info = data; diff --git a/src/modules/spa/spa-monitor.c b/src/modules/spa/spa-monitor.c index 39c1b2317..66b726e10 100644 --- a/src/modules/spa/spa-monitor.c +++ b/src/modules/spa/spa-monitor.c @@ -114,6 +114,7 @@ static void add_item(struct pw_spa_monitor *this, struct spa_monitor_item *item) } if ((res = spa_handle_get_interface(handle, t->spa_clock, &clock_iface)) < 0) { pw_log_info("no CLOCK interface: %d", res); + clock_iface = NULL; } diff --git a/src/modules/spa/spa-node.c b/src/modules/spa/spa-node.c index b55979838..d350bfd2b 100644 --- a/src/modules/spa/spa-node.c +++ b/src/modules/spa/spa-node.c @@ -150,7 +150,7 @@ make_port(struct impl *impl, enum pw_direction direction, uint32_t port_id) struct pw_port *port; struct port *p; - port = pw_port_new(direction, port_id, sizeof(struct port)); + port = pw_port_new(direction, port_id, NULL, sizeof(struct port)); if (port == NULL) return NULL; diff --git a/src/pipewire/core.c b/src/pipewire/core.c index 8f07fbdcd..b42a8c388 100644 --- a/src/pipewire/core.c +++ b/src/pipewire/core.c @@ -320,6 +320,13 @@ struct pw_core *pw_core_new(struct pw_loop *main_loop, struct pw_properties *pro if (this == NULL) return NULL; + if (properties == NULL) + properties = pw_properties_new(NULL, NULL); + if (properties == NULL) + goto no_mem; + + this->properties = properties; + this->data_loop_impl = pw_data_loop_new(properties); if (this->data_loop_impl == NULL) goto no_data_loop; @@ -355,23 +362,21 @@ struct pw_core *pw_core_new(struct pw_loop *main_loop, struct pw_properties *pro spa_list_init(&this->link_list); spa_hook_list_init(&this->listener_list); + if ((name = pw_properties_get(properties, "pipewire.core.name")) == NULL) { + pw_properties_setf(properties, + "pipewire.core.name", "pipewire-%s-%d", + pw_get_user_name(), getpid()); + name = pw_properties_get(properties, "pipewire.core.name"); + } + this->info.change_mask = 0; this->info.user_name = pw_get_user_name(); this->info.host_name = pw_get_host_name(); this->info.version = SPA_STRINGIFY(PW_VERSION_CORE); srandom(time(NULL)); this->info.cookie = random(); - this->info.props = this->properties ? &this->properties->dict : NULL; - - if (properties == NULL) - properties = pw_properties_new(NULL, NULL); - if ((name = pw_properties_get(properties, "pipewire.core.name")) == NULL) { - pw_properties_setf(properties, - "pipewire.core.name", "pipewire-%s-%d", - pw_get_user_name(), getpid()); - } - this->info.name = pw_properties_get(properties, "pipewire.core.name"); - this->properties = properties; + this->info.props = &properties->dict; + this->info.name = name; this->global = pw_core_add_global(this, NULL, @@ -382,6 +387,7 @@ struct pw_core *pw_core_new(struct pw_loop *main_loop, struct pw_properties *pro this); return this; + no_mem: no_data_loop: free(this); return NULL; @@ -472,26 +478,19 @@ const struct pw_properties *pw_core_get_properties(struct pw_core *core) void pw_core_update_properties(struct pw_core *core, const struct spa_dict *dict) { struct pw_resource *resource; + uint32_t i; - if (core->properties == NULL) { - if (dict) - core->properties = pw_properties_new_dict(dict); - } else if (dict != &core->properties->dict) { - uint32_t i; - - for (i = 0; i < dict->n_items; i++) - pw_properties_set(core->properties, - dict->items[i].key, dict->items[i].value); - } + for (i = 0; i < dict->n_items; i++) + pw_properties_set(core->properties, dict->items[i].key, dict->items[i].value); core->info.change_mask = PW_CORE_CHANGE_MASK_PROPS; - core->info.props = core->properties ? &core->properties->dict : NULL; + core->info.props = &core->properties->dict; spa_hook_list_call(&core->listener_list, struct pw_core_events, info_changed, &core->info); - spa_list_for_each(resource, &core->resource_list, link) { + spa_list_for_each(resource, &core->resource_list, link) pw_core_resource_info(resource, &core->info); - } + core->info.change_mask = 0; } @@ -545,6 +544,9 @@ struct pw_port *pw_core_find_port(struct pw_core *core, if (n->global == NULL) continue; + if (other_port->node == n) + continue; + pw_log_debug("node id \"%d\"", n->global->id); if (have_id) { diff --git a/src/pipewire/data-loop.c b/src/pipewire/data-loop.c index 346c58d98..f8646f31e 100644 --- a/src/pipewire/data-loop.c +++ b/src/pipewire/data-loop.c @@ -90,7 +90,7 @@ static void *do_loop(void *user_data) } -static void do_stop(struct spa_loop_utils *utils, struct spa_source *source, uint64_t count, void *data) +static void do_stop(void *data, uint64_t count) { struct pw_data_loop *this = data; this->running = false; diff --git a/src/pipewire/introspect.c b/src/pipewire/introspect.c index e6d0d7cc8..2a2fb2acd 100644 --- a/src/pipewire/introspect.c +++ b/src/pipewire/introspect.c @@ -187,16 +187,16 @@ struct pw_node_info *pw_node_info_update(struct pw_node_info *info, } info->change_mask = update->change_mask; - if (update->change_mask & (1 << 0)) { + if (update->change_mask & PW_NODE_CHANGE_MASK_NAME) { if (info->name) free((void *) info->name); info->name = update->name ? strdup(update->name) : NULL; } - if (update->change_mask & (1 << 1)) { + if (update->change_mask & PW_NODE_CHANGE_MASK_INPUT_PORTS) { info->max_input_ports = update->max_input_ports; info->n_input_ports = update->n_input_ports; } - if (update->change_mask & (1 << 2)) { + if (update->change_mask & PW_NODE_CHANGE_MASK_INPUT_FORMATS) { for (i = 0; i < info->n_input_formats; i++) free(info->input_formats[i]); info->n_input_formats = update->n_input_formats; @@ -212,11 +212,11 @@ struct pw_node_info *pw_node_info_update(struct pw_node_info *info, info->input_formats[i] = spa_format_copy(update->input_formats[i]); } } - if (update->change_mask & (1 << 3)) { + if (update->change_mask & PW_NODE_CHANGE_MASK_OUTPUT_PORTS) { info->max_output_ports = update->max_output_ports; info->n_output_ports = update->n_output_ports; } - if (update->change_mask & (1 << 4)) { + if (update->change_mask & PW_NODE_CHANGE_MASK_OUTPUT_FORMATS) { for (i = 0; i < info->n_output_formats; i++) free(info->output_formats[i]); info->n_output_formats = update->n_output_formats; @@ -233,13 +233,13 @@ struct pw_node_info *pw_node_info_update(struct pw_node_info *info, } } - if (update->change_mask & (1 << 5)) { + if (update->change_mask & PW_NODE_CHANGE_MASK_STATE) { info->state = update->state; if (info->error) free((void *) info->error); info->error = update->error ? strdup(update->error) : NULL; } - if (update->change_mask & (1 << 6)) { + if (update->change_mask & PW_NODE_CHANGE_MASK_PROPS) { if (info->props) pw_spa_dict_destroy(info->props); info->props = pw_spa_dict_copy(update->props); diff --git a/src/pipewire/introspect.h b/src/pipewire/introspect.h index 52238e45b..7433440dd 100644 --- a/src/pipewire/introspect.h +++ b/src/pipewire/introspect.h @@ -132,8 +132,15 @@ void pw_client_info_free(struct pw_client_info *info); /** The node information. Extra information can be added in later versions \memberof pw_introspect */ struct pw_node_info { +#define PW_NODE_CHANGE_MASK_NAME (1 << 0) +#define PW_NODE_CHANGE_MASK_INPUT_PORTS (1 << 1) +#define PW_NODE_CHANGE_MASK_INPUT_FORMATS (1 << 2) +#define PW_NODE_CHANGE_MASK_OUTPUT_PORTS (1 << 3) +#define PW_NODE_CHANGE_MASK_OUTPUT_FORMATS (1 << 4) +#define PW_NODE_CHANGE_MASK_STATE (1 << 5) +#define PW_NODE_CHANGE_MASK_PROPS (1 << 6) uint64_t change_mask; /**< bitfield of changed fields since last call */ - const char *name; /**< name the node, suitable for display */ + const char *name; /**< name the node, suitable for display */ uint32_t max_input_ports; /**< maximum number of inputs */ uint32_t n_input_ports; /**< number of inputs */ uint32_t n_input_formats; /**< number of input formats */ diff --git a/src/pipewire/link.c b/src/pipewire/link.c index 4b084ee69..7a8cd9e5d 100644 --- a/src/pipewire/link.c +++ b/src/pipewire/link.c @@ -1021,7 +1021,8 @@ struct pw_link *pw_link_new(struct pw_core *core, struct pw_port *input, struct spa_format *format_filter, struct pw_properties *properties, - char **error) + char **error, + size_t user_data_size) { struct impl *impl; struct pw_link *this; @@ -1033,13 +1034,16 @@ struct pw_link *pw_link_new(struct pw_core *core, if (pw_link_find(output, input)) goto link_exists; - impl = calloc(1, sizeof(struct impl)); + impl = calloc(1, sizeof(struct impl) + user_data_size); if (impl == NULL) goto no_mem; this = &impl->this; pw_log_debug("link %p: new", this); + if (user_data_size > 0) + this->user_data = SPA_MEMBER(impl, sizeof(struct impl), void); + impl->work = pw_work_queue_new(core->main_loop); this->core = core; @@ -1157,6 +1161,7 @@ void pw_link_add_listener(struct pw_link *link, const struct pw_link_events *events, void *data) { + pw_log_debug("link %p: add listener %p", link, listener); spa_hook_list_append(&link->listener_list, listener, events, data); } @@ -1176,6 +1181,11 @@ struct pw_core *pw_link_get_core(struct pw_link *link) return link->core; } +void *pw_link_get_user_data(struct pw_link *link) +{ + return link->user_data; +} + const struct pw_link_info *pw_link_get_info(struct pw_link *link) { return &link->info; diff --git a/src/pipewire/link.h b/src/pipewire/link.h index f7b0880d2..1eacd57c6 100644 --- a/src/pipewire/link.h +++ b/src/pipewire/link.h @@ -71,7 +71,8 @@ pw_link_new(struct pw_core *core, /**< the core object */ struct pw_port *input, /**< an input port */ struct spa_format *format_filter, /**< an optional format filter */ struct pw_properties *properties /**< extra properties */, - char **error /**< error string when result is NULL */); + char **error, /**< error string when result is NULL */ + size_t user_data_size /**< extra user data size */); /** Destroy a link \memberof pw_link */ void pw_link_destroy(struct pw_link *link); @@ -84,6 +85,8 @@ void pw_link_add_listener(struct pw_link *link, struct pw_core *pw_link_get_core(struct pw_link *link); +void *pw_link_get_user_data(struct pw_link *link); + const struct pw_link_info *pw_link_get_info(struct pw_link *link); /** Get the global of the link */ diff --git a/src/pipewire/node.c b/src/pipewire/node.c index 3f308e4a0..5c4f359ff 100644 --- a/src/pipewire/node.c +++ b/src/pipewire/node.c @@ -447,11 +447,30 @@ struct pw_global *pw_node_get_global(struct pw_node *node) return node->global; } -struct pw_properties *pw_node_get_properties(struct pw_node *node) +const struct pw_properties *pw_node_get_properties(struct pw_node *node) { return node->properties; } +void pw_node_update_properties(struct pw_node *node, const struct spa_dict *dict) +{ + struct pw_resource *resource; + uint32_t i; + + for (i = 0; i < dict->n_items; i++) + pw_properties_set(node->properties, dict->items[i].key, dict->items[i].value); + + node->info.props = &node->properties->dict; + + node->info.change_mask = PW_NODE_CHANGE_MASK_PROPS; + spa_hook_list_call(&node->listener_list, struct pw_node_events, info_changed, &node->info); + + spa_list_for_each(resource, &node->resource_list, link) + pw_node_resource_info(resource, &node->info); + + node->info.change_mask = 0; +} + void pw_node_set_implementation(struct pw_node *node, const struct pw_node_implementation *implementation, void *data) @@ -759,7 +778,7 @@ void pw_node_update_state(struct pw_node *node, enum pw_node_state state, char * spa_hook_list_call(&node->listener_list, struct pw_node_events, state_changed, old, state, error); - node->info.change_mask |= 1 << 5; + node->info.change_mask |= PW_NODE_CHANGE_MASK_STATE; spa_hook_list_call(&node->listener_list, struct pw_node_events, info_changed, &node->info); spa_list_for_each(resource, &node->resource_list, link) diff --git a/src/pipewire/node.h b/src/pipewire/node.h index 7e5dc4532..a0741776e 100644 --- a/src/pipewire/node.h +++ b/src/pipewire/node.h @@ -129,7 +129,9 @@ struct pw_resource *pw_node_get_owner(struct pw_node *node); struct pw_global *pw_node_get_global(struct pw_node *node); -struct pw_properties *pw_node_get_properties(struct pw_node *node); +const struct pw_properties *pw_node_get_properties(struct pw_node *node); + +void pw_node_update_properties(struct pw_node *node, const struct spa_dict *dict); void pw_node_set_implementation(struct pw_node *node, const struct pw_node_implementation *implementation, diff --git a/src/pipewire/port.c b/src/pipewire/port.c index 6a04a6b73..4fa3b6388 100644 --- a/src/pipewire/port.c +++ b/src/pipewire/port.c @@ -141,6 +141,7 @@ static const struct spa_graph_port_callbacks schedule_mix_port = { struct pw_port *pw_port_new(enum pw_direction direction, uint32_t port_id, + struct pw_properties *properties, size_t user_data_size) { struct impl *impl; @@ -153,8 +154,14 @@ struct pw_port *pw_port_new(enum pw_direction direction, this = &impl->this; pw_log_debug("port %p: new", this); + if (properties == NULL) + properties = pw_properties_new(NULL, NULL); + if (properties == NULL) + goto no_mem; + this->direction = direction; this->port_id = port_id; + this->properties = properties; this->state = PW_PORT_STATE_INIT; this->io.status = SPA_RESULT_OK; this->io.buffer_id = SPA_ID_INVALID; @@ -166,6 +173,8 @@ struct pw_port *pw_port_new(enum pw_direction direction, spa_hook_list_init(&this->listener_list); + spa_graph_port_set_callbacks(&this->rt.port, NULL, this); + spa_graph_port_init(&this->rt.port, this->direction, this->port_id, @@ -188,6 +197,10 @@ struct pw_port *pw_port_new(enum pw_direction direction, &schedule_tee_port, this); return this; + + no_mem: + free(impl); + return NULL; } enum pw_direction pw_port_get_direction(struct pw_port *port) @@ -200,6 +213,21 @@ uint32_t pw_port_get_id(struct pw_port *port) return port->port_id; } +const struct pw_properties *pw_port_get_properties(struct pw_port *port) +{ + return port->properties; +} + +void pw_port_update_properties(struct pw_port *port, const struct spa_dict *dict) +{ + uint32_t i; + for (i = 0; i < dict->n_items; i++) + pw_properties_set(port->properties, dict->items[i].key, dict->items[i].value); + + spa_hook_list_call(&port->listener_list, struct pw_port_events, + properties_changed, port->properties); +} + struct pw_node *pw_port_get_node(struct pw_port *port) { return port->node; diff --git a/src/pipewire/port.h b/src/pipewire/port.h index 144b53244..71646c253 100644 --- a/src/pipewire/port.h +++ b/src/pipewire/port.h @@ -94,6 +94,8 @@ struct pw_port_events { void (*destroy) (void *data); void (*state_changed) (void *data, enum pw_port_state state); + + void (*properties_changed) (void *data, const struct pw_properties *properties); }; /** Create a new port \memberof pw_port @@ -101,11 +103,16 @@ struct pw_port_events { struct pw_port * pw_port_new(enum pw_direction direction, uint32_t port_id, + struct pw_properties *properties, size_t user_data_size); /** Get the port direction */ enum pw_direction pw_port_get_direction(struct pw_port *port); +const struct pw_properties *pw_port_get_properties(struct pw_port *port); + +void pw_port_update_properties(struct pw_port *port, const struct spa_dict *dict); + /** Get the port id */ uint32_t pw_port_get_id(struct pw_port *port); diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 6e38686ab..d31b2c9f2 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -166,6 +166,8 @@ struct pw_link { struct spa_graph_port out_port; struct spa_graph_port in_port; } rt; + + void *user_data; }; struct pw_module { @@ -230,6 +232,7 @@ struct pw_port { enum pw_direction direction; /**< port direction */ uint32_t port_id; /**< port id */ + struct pw_properties *properties; enum pw_port_state state; /**< state of the port */ diff --git a/src/pipewire/remote.c b/src/pipewire/remote.c index 2a43553fd..ee6912d7f 100644 --- a/src/pipewire/remote.c +++ b/src/pipewire/remote.c @@ -445,8 +445,7 @@ static void handle_rtnode_message(struct pw_proxy *proxy, struct pw_client_node_ } static void -on_rtsocket_condition(struct spa_loop_utils *utils, - struct spa_source *source, int fd, enum spa_io mask, void *user_data) +on_rtsocket_condition(void *user_data, int fd, enum spa_io mask) { struct pw_proxy *proxy = user_data; struct node_data *data = proxy->user_data; diff --git a/src/pipewire/stream.c b/src/pipewire/stream.c index 446db4035..681e93ff7 100644 --- a/src/pipewire/stream.c +++ b/src/pipewire/stream.c @@ -444,7 +444,7 @@ static void do_node_init(struct pw_stream *stream) add_async_complete(stream, 0, SPA_RESULT_OK); } -static void on_timeout(struct spa_loop_utils *utils, struct spa_source *source, void *data) +static void on_timeout(void *data, uint64_t expirations) { struct pw_stream *stream = data; add_request_clock_update(stream); @@ -544,8 +544,7 @@ static void handle_rtnode_message(struct pw_stream *stream, struct pw_client_nod } static void -on_rtsocket_condition(struct spa_loop_utils *utils, - struct spa_source *source, int fd, enum spa_io mask, void *data) +on_rtsocket_condition(void *data, int fd, enum spa_io mask) { struct pw_stream *stream = data; struct stream *impl = SPA_CONTAINER_OF(stream, struct stream, this); diff --git a/src/pipewire/thread-loop.c b/src/pipewire/thread-loop.c index 97932db16..4e05cfa22 100644 --- a/src/pipewire/thread-loop.c +++ b/src/pipewire/thread-loop.c @@ -63,7 +63,7 @@ static const struct spa_loop_control_hooks impl_hooks = { after, }; -static void do_stop(struct spa_loop_utils *utils, struct spa_source *source, uint64_t count, void *data) +static void do_stop(void *data, uint64_t count) { struct pw_thread_loop *this = data; this->running = false; diff --git a/src/pipewire/work-queue.c b/src/pipewire/work-queue.c index e0337b340..b551e345f 100644 --- a/src/pipewire/work-queue.c +++ b/src/pipewire/work-queue.c @@ -46,8 +46,7 @@ struct pw_work_queue { }; /** \endcond */ -static void process_work_queue(struct spa_loop_utils *utils, struct spa_source *source, - uint64_t count, void *data) +static void process_work_queue(void *data, uint64_t count) { struct pw_work_queue *this = data; struct work_item *item, *tmp;