diff --git a/src/gst/gstpipewiredeviceprovider.c b/src/gst/gstpipewiredeviceprovider.c index 6fa0341c0..fefdb57be 100644 --- a/src/gst/gstpipewiredeviceprovider.c +++ b/src/gst/gstpipewiredeviceprovider.c @@ -200,30 +200,24 @@ new_node (GstPipeWireDeviceProvider *self, const struct pw_node_info *info, uint const gchar *klass = NULL; const struct spa_dict_item *item; GstPipeWireDeviceType type; - int i; - struct pw_type *t = self->type; caps = gst_caps_new_empty (); +#if 0 + int i; + struct pw_type *t = self->type; + for (i = 0; i < info->n_params; i++) { + if (!spa_pod_is_object_id(info->params[i], t->param.idEnumFormat)) + continue; + GstCaps *c1 = gst_caps_from_format (info->params[i], t->map); + if (c1) + gst_caps_append (caps, c1); + } +#endif + if (info->max_input_ports > 0 && info->max_output_ports == 0) { type = GST_PIPEWIRE_DEVICE_TYPE_SINK; - - for (i = 0; i < info->n_input_params; i++) { - if (!spa_pod_is_object_id(info->input_params[i], t->param.idEnumFormat)) - continue; - GstCaps *c1 = gst_caps_from_format (info->input_params[i], t->map); - if (c1) - gst_caps_append (caps, c1); - } - } - else if (info->max_output_ports > 0 && info->max_input_ports == 0) { + } else if (info->max_output_ports > 0 && info->max_input_ports == 0) { type = GST_PIPEWIRE_DEVICE_TYPE_SOURCE; - for (i = 0; i < info->n_output_params; i++) { - if (!spa_pod_is_object_id(info->output_params[i], t->param.idEnumFormat)) - continue; - GstCaps *c1 = gst_caps_from_format (info->output_params[i], t->map); - if (c1) - gst_caps_append (caps, c1); - } } else { gst_caps_unref(caps); return NULL; diff --git a/src/modules/module-audio-dsp.c b/src/modules/module-audio-dsp.c index 04e1e991c..b7c49c676 100644 --- a/src/modules/module-audio-dsp.c +++ b/src/modules/module-audio-dsp.c @@ -740,7 +740,16 @@ static struct pw_node *make_node(struct impl *impl, const struct pw_properties * char node_name[128]; int i; - snprintf(node_name, sizeof(node_name), "system%d", impl->node_count++); + if ((alias = pw_properties_get(props, "alsa.device")) == NULL) + goto error; + + snprintf(node_name, sizeof(node_name), "system_%s", alias); + for (i = 0; node_name[i]; i++) { + if (node_name[i] == ':') + node_name[i] = '_'; + } + if ((alias = pw_properties_get(props, "alsa.card")) == NULL) + goto error; node = pw_node_new(impl->core, node_name, NULL, sizeof(struct node)); if (node == NULL) @@ -755,9 +764,6 @@ static struct pw_node *make_node(struct impl *impl, const struct pw_properties * n->buffer_size = 1024 / sizeof(float); pw_node_set_implementation(node, &n->node_impl); - if ((alias = pw_properties_get(props, "alsa.card")) == NULL) - goto error; - p = make_port(n, direction, 0, 0, NULL); if (p == NULL) goto error_free_node; diff --git a/src/modules/module-protocol-native/protocol-native.c b/src/modules/module-protocol-native/protocol-native.c index bf2977d45..770af5d5e 100644 --- a/src/modules/module-protocol-native/protocol-native.c +++ b/src/modules/module-protocol-native/protocol-native.c @@ -728,6 +728,8 @@ static void node_marshal_info(void *object, struct pw_node_info *info) b = pw_protocol_native_begin_resource(resource, PW_NODE_PROXY_EVENT_INFO); + n_items = info->props ? info->props->n_items : 0; + spa_pod_builder_add(b, "[", "i", info->id, @@ -735,24 +737,11 @@ static void node_marshal_info(void *object, struct pw_node_info *info) "s", info->name, "i", info->max_input_ports, "i", info->n_input_ports, - "i", info->n_input_params, NULL); - - for (i = 0; i < info->n_input_params; i++) - spa_pod_builder_add(b, "P", info->input_params[i], NULL); - - spa_pod_builder_add(b, "i", info->max_output_ports, "i", info->n_output_ports, - "i", info->n_output_params, 0); - - for (i = 0; i < info->n_output_params; i++) - spa_pod_builder_add(b, "P", info->output_params[i], NULL); - - n_items = info->props ? info->props->n_items : 0; - - spa_pod_builder_add(b, "i", info->state, - "s", info->error, "i", n_items, NULL); + "s", info->error, + "i", n_items, NULL); for (i = 0; i < n_items; i++) { spa_pod_builder_add(b, @@ -780,29 +769,11 @@ static int node_demarshal_info(void *object, void *data, size_t size) "s", &info.name, "i", &info.max_input_ports, "i", &info.n_input_ports, - "i", &info.n_input_params, NULL) < 0) - return -EINVAL; - - info.input_params = alloca(info.n_input_params * sizeof(struct spa_pod *)); - for (i = 0; i < info.n_input_params; i++) - if (spa_pod_parser_get(&prs, "P", &info.input_params[i], NULL) < 0) - return -EINVAL; - - if (spa_pod_parser_get(&prs, - "i", &info.max_output_ports, - "i", &info.n_output_ports, - "i", &info.n_output_params, NULL) < 0) - return -EINVAL; - - info.output_params = alloca(info.n_output_params * sizeof(struct spa_pod *)); - for (i = 0; i < info.n_output_params; i++) - if (spa_pod_parser_get(&prs, "P", &info.output_params[i], NULL) < 0) - return -EINVAL; - - if (spa_pod_parser_get(&prs, - "i", &info.state, - "s", &info.error, - "i", &props.n_items, NULL) < 0) + "i", &info.max_output_ports, + "i", &info.n_output_ports, + "i", &info.state, + "s", &info.error, + "i", &props.n_items, NULL) < 0) return -EINVAL; info.props = &props; @@ -817,6 +788,198 @@ static int node_demarshal_info(void *object, void *data, size_t size) return 0; } +static void node_marshal_param(void *object, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_NODE_PROXY_EVENT_PARAM); + + spa_pod_builder_struct(b, "I", id, "i", index, "i", next, "P", param); + + pw_protocol_native_end_resource(resource, b); +} + +static int node_demarshal_param(void *object, void *data, size_t size) +{ + struct pw_proxy *proxy = object; + struct spa_pod_parser prs; + uint32_t id, index, next; + struct spa_pod *param; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, + "[ I", &id, + "i", &index, + "i", &next, + "P", ¶m, NULL) < 0) + return -EINVAL; + + pw_proxy_notify(proxy, struct pw_node_proxy_events, param, id, index, next, param); + return 0; +} + +static void node_marshal_enum_params(void *object, uint32_t id, uint32_t index, uint32_t num, + const struct spa_pod *filter) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_proxy(proxy, PW_NODE_PROXY_METHOD_ENUM_PARAMS); + + spa_pod_builder_struct(b, + "I", id, + "i", index, + "i", num, + "P", filter); + + pw_protocol_native_end_proxy(proxy, b); +} + +static int node_demarshal_enum_params(void *object, void *data, size_t size) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + uint32_t id, index, num; + struct spa_pod *filter; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, + "[ I", &id, + "i", &index, + "i", &num, + "P", &filter, NULL) < 0) + return -EINVAL; + + pw_resource_do(resource, struct pw_node_proxy_methods, enum_params, id, index, num, filter); + return 0; +} + +static void port_marshal_info(void *object, struct pw_port_info *info) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + uint32_t i, n_items; + + b = pw_protocol_native_begin_resource(resource, PW_PORT_PROXY_EVENT_INFO); + + n_items = info->props ? info->props->n_items : 0; + + spa_pod_builder_add(b, + "[", + "i", info->id, + "l", info->change_mask, + "s", info->name, + "i", n_items, NULL); + + for (i = 0; i < n_items; i++) { + spa_pod_builder_add(b, + "s", info->props->items[i].key, + "s", info->props->items[i].value, NULL); + } + spa_pod_builder_add(b, "]", NULL); + + pw_protocol_native_end_resource(resource, b); +} + +static int port_demarshal_info(void *object, void *data, size_t size) +{ + struct pw_proxy *proxy = object; + struct spa_pod_parser prs; + struct spa_dict props; + struct pw_port_info info; + int i; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, + "[" + "i", &info.id, + "l", &info.change_mask, + "s", &info.name, + "i", &props.n_items, NULL) < 0) + return -EINVAL; + + info.props = &props; + props.items = alloca(props.n_items * sizeof(struct spa_dict_item)); + for (i = 0; i < props.n_items; i++) { + if (spa_pod_parser_get(&prs, + "s", &props.items[i].key, + "s", &props.items[i].value, NULL) < 0) + return -EINVAL; + } + pw_proxy_notify(proxy, struct pw_port_proxy_events, info, &info); + return 0; +} + +static void port_marshal_param(void *object, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct pw_resource *resource = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_resource(resource, PW_PORT_PROXY_EVENT_PARAM); + + spa_pod_builder_struct(b, "I", id, "i", index, "i", next, "P", param); + + pw_protocol_native_end_resource(resource, b); +} + +static int port_demarshal_param(void *object, void *data, size_t size) +{ + struct pw_proxy *proxy = object; + struct spa_pod_parser prs; + uint32_t id, index, next; + struct spa_pod *param; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, + "[ I", &id, + "i", &index, + "i", &next, + "P", ¶m, NULL) < 0) + return -EINVAL; + + pw_proxy_notify(proxy, struct pw_port_proxy_events, param, id, index, next, param); + return 0; +} + +static void port_marshal_enum_params(void *object, uint32_t id, uint32_t index, uint32_t num, + const struct spa_pod *filter) +{ + struct pw_proxy *proxy = object; + struct spa_pod_builder *b; + + b = pw_protocol_native_begin_proxy(proxy, PW_PORT_PROXY_METHOD_ENUM_PARAMS); + + spa_pod_builder_struct(b, + "I", id, + "i", index, + "i", num, + "P", filter); + + pw_protocol_native_end_proxy(proxy, b); +} + +static int port_demarshal_enum_params(void *object, void *data, size_t size) +{ + struct pw_resource *resource = object; + struct spa_pod_parser prs; + uint32_t id, index, num; + struct spa_pod *filter; + + spa_pod_parser_init(&prs, data, size, 0); + if (spa_pod_parser_get(&prs, + "[ I", &id, + "i", &index, + "i", &num, + "P", &filter, NULL) < 0) + return -EINVAL; + + pw_resource_do(resource, struct pw_port_proxy_methods, enum_params, id, index, num, filter); + return 0; +} + static void client_marshal_info(void *object, struct pw_client_info *info) { struct pw_resource *resource = object; @@ -1116,24 +1279,69 @@ const struct pw_protocol_marshal pw_protocol_native_factory_marshal = { PW_FACTORY_PROXY_EVENT_NUM, }; +static const struct pw_node_proxy_methods pw_protocol_native_node_method_marshal = { + PW_VERSION_NODE_PROXY_METHODS, + &node_marshal_enum_params, +}; + +static const struct pw_protocol_native_demarshal pw_protocol_native_node_method_demarshal[] = { + { &node_demarshal_enum_params, PW_PROTOCOL_NATIVE_REMAP, }, +}; + static const struct pw_node_proxy_events pw_protocol_native_node_event_marshal = { PW_VERSION_NODE_PROXY_EVENTS, &node_marshal_info, + &node_marshal_param, }; static const struct pw_protocol_native_demarshal pw_protocol_native_node_event_demarshal[] = { - { &node_demarshal_info, PW_PROTOCOL_NATIVE_REMAP, } + { &node_demarshal_info, PW_PROTOCOL_NATIVE_REMAP, }, + { &node_demarshal_param, PW_PROTOCOL_NATIVE_REMAP, } }; static const struct pw_protocol_marshal pw_protocol_native_node_marshal = { PW_TYPE_INTERFACE__Node, PW_VERSION_NODE, - NULL, NULL, 0, + &pw_protocol_native_node_method_marshal, + pw_protocol_native_node_method_demarshal, + PW_NODE_PROXY_METHOD_NUM, &pw_protocol_native_node_event_marshal, pw_protocol_native_node_event_demarshal, PW_NODE_PROXY_EVENT_NUM, }; + +static const struct pw_port_proxy_methods pw_protocol_native_port_method_marshal = { + PW_VERSION_PORT_PROXY_METHODS, + &port_marshal_enum_params, +}; + +static const struct pw_protocol_native_demarshal pw_protocol_native_port_method_demarshal[] = { + { &port_demarshal_enum_params, PW_PROTOCOL_NATIVE_REMAP, }, +}; + +static const struct pw_port_proxy_events pw_protocol_native_port_event_marshal = { + PW_VERSION_PORT_PROXY_EVENTS, + &port_marshal_info, + &port_marshal_param, +}; + +static const struct pw_protocol_native_demarshal pw_protocol_native_port_event_demarshal[] = { + { &port_demarshal_info, PW_PROTOCOL_NATIVE_REMAP, }, + { &port_demarshal_param, PW_PROTOCOL_NATIVE_REMAP, } +}; + +static const struct pw_protocol_marshal pw_protocol_native_port_marshal = { + PW_TYPE_INTERFACE__Port, + PW_VERSION_PORT, + &pw_protocol_native_port_method_marshal, + pw_protocol_native_port_method_demarshal, + PW_PORT_PROXY_METHOD_NUM, + &pw_protocol_native_port_event_marshal, + pw_protocol_native_port_event_demarshal, + PW_PORT_PROXY_EVENT_NUM, +}; + static const struct pw_client_proxy_events pw_protocol_native_client_event_marshal = { PW_VERSION_CLIENT_PROXY_EVENTS, &client_marshal_info, @@ -1176,6 +1384,7 @@ void pw_protocol_native_init(struct pw_protocol *protocol) pw_protocol_add_marshal(protocol, &pw_protocol_native_registry_marshal); pw_protocol_add_marshal(protocol, &pw_protocol_native_module_marshal); pw_protocol_add_marshal(protocol, &pw_protocol_native_node_marshal); + pw_protocol_add_marshal(protocol, &pw_protocol_native_port_marshal); pw_protocol_add_marshal(protocol, &pw_protocol_native_factory_marshal); pw_protocol_add_marshal(protocol, &pw_protocol_native_client_marshal); pw_protocol_add_marshal(protocol, &pw_protocol_native_link_marshal); diff --git a/src/pipewire/core.c b/src/pipewire/core.c index aaf1020d6..da3a1ebf4 100644 --- a/src/pipewire/core.c +++ b/src/pipewire/core.c @@ -65,7 +65,8 @@ static void registry_bind(void *object, uint32_t id, pw_log_debug("global %p: bind global id %d, iface %s to %d", global, id, spa_type_map_get_type(core->type.map, type), new_id); - pw_global_bind(global, client, permissions, version, new_id); + if (pw_global_bind(global, client, permissions, version, new_id) < 0) + goto exit; return; diff --git a/src/pipewire/interfaces.h b/src/pipewire/interfaces.h index 484530901..3794f702d 100644 --- a/src/pipewire/interfaces.h +++ b/src/pipewire/interfaces.h @@ -484,7 +484,8 @@ pw_module_proxy_add_listener(struct pw_module_proxy *module, #define PW_VERSION_NODE 0 #define PW_NODE_PROXY_EVENT_INFO 0 -#define PW_NODE_PROXY_EVENT_NUM 1 +#define PW_NODE_PROXY_EVENT_PARAM 1 +#define PW_NODE_PROXY_EVENT_NUM 2 /** Node events */ struct pw_node_proxy_events { @@ -496,6 +497,19 @@ struct pw_node_proxy_events { * \param info info about the node */ void (*info) (void *object, struct pw_node_info *info); + /** + * Notify a node param + * + * Event emited as a result of the enum_params method. + * + * \param id the param id + * \param index the param index + * \param next the param index of the next param + * \param param the parameter + */ + void (*param) (void *object, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param); }; static inline void @@ -508,12 +522,44 @@ pw_node_proxy_add_listener(struct pw_node_proxy *node, } #define pw_node_resource_info(r,...) pw_resource_notify(r,struct pw_node_proxy_events,info,__VA_ARGS__) +#define pw_node_resource_param(r,...) pw_resource_notify(r,struct pw_node_proxy_events,param,__VA_ARGS__) +#define PW_NODE_PROXY_METHOD_ENUM_PARAMS 0 +#define PW_NODE_PROXY_METHOD_NUM 1 + +/** Node methods */ +struct pw_node_proxy_methods { +#define PW_VERSION_NODE_PROXY_METHODS 0 + uint32_t version; + /** + * Enumerate node parameters + * + * Start enumeration of node parameters. For each param, a + * param event will be emited. + * + * \param id the parameter id to enum or SPA_ID_INVALID for all + * \param start the start index or 0 for the first param + * \param num the maximum number of params to retrieve + * \param filter a param filter or NULL + */ + void (*enum_params) (void *object, uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter); +}; + +/** Registry */ +static inline void +pw_node_proxy_enum_params(struct pw_node_proxy *node, uint32_t id, uint32_t index, + uint32_t num, const struct spa_pod *filter) +{ + pw_proxy_do((struct pw_proxy*)node, struct pw_node_proxy_methods, enum_params, + id, index, num, filter); +} #define PW_VERSION_PORT 0 #define PW_PORT_PROXY_EVENT_INFO 0 -#define PW_PORT_PROXY_EVENT_NUM 1 +#define PW_PORT_PROXY_EVENT_PARAM 1 +#define PW_PORT_PROXY_EVENT_NUM 2 /** Port events */ struct pw_port_proxy_events { @@ -525,6 +571,19 @@ struct pw_port_proxy_events { * \param info info about the port */ void (*info) (void *object, struct pw_port_info *info); + /** + * Notify a port param + * + * Event emited as a result of the enum_params method. + * + * \param id the param id + * \param index the param index + * \param next the param index of the next param + * \param param the parameter + */ + void (*param) (void *object, + uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param); }; static inline void @@ -537,6 +596,38 @@ pw_port_proxy_add_listener(struct pw_port_proxy *port, } #define pw_port_resource_info(r,...) pw_resource_notify(r,struct pw_port_proxy_events,info,__VA_ARGS__) +#define pw_port_resource_param(r,...) pw_resource_notify(r,struct pw_port_proxy_events,param,__VA_ARGS__) + +#define PW_PORT_PROXY_METHOD_ENUM_PARAMS 0 +#define PW_PORT_PROXY_METHOD_NUM 1 + +/** Port methods */ +struct pw_port_proxy_methods { +#define PW_VERSION_PORT_PROXY_METHODS 0 + uint32_t version; + /** + * Enumerate port parameters + * + * Start enumeration of port parameters. For each param, a + * param event will be emited. + * + * \param id the parameter id to enumerate + * \param start the start index or 0 for the first param + * \param num the maximum number of params to retrieve + * \param filter a param filter or NULL + */ + void (*enum_params) (void *object, uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter); +}; + +/** Registry */ +static inline void +pw_port_proxy_enum_params(struct pw_port_proxy *port, uint32_t id, uint32_t index, + uint32_t num, const struct spa_pod *filter) +{ + pw_proxy_do((struct pw_proxy*)port, struct pw_port_proxy_methods, enum_params, + id, index, num, filter); +} #define PW_VERSION_FACTORY 0 diff --git a/src/pipewire/introspect.c b/src/pipewire/introspect.c index f6f884c6a..d9aed7fa8 100644 --- a/src/pipewire/introspect.c +++ b/src/pipewire/introspect.c @@ -177,7 +177,6 @@ void pw_core_info_free(struct pw_core_info *info) struct pw_node_info *pw_node_info_update(struct pw_node_info *info, const struct pw_node_info *update) { - int i; if (update == NULL) return info; @@ -199,42 +198,10 @@ struct pw_node_info *pw_node_info_update(struct pw_node_info *info, info->max_input_ports = update->max_input_ports; info->n_input_ports = update->n_input_ports; } - if (update->change_mask & PW_NODE_CHANGE_MASK_INPUT_PARAMS) { - for (i = 0; i < info->n_input_params; i++) - free(info->input_params[i]); - info->n_input_params = update->n_input_params; - if (info->n_input_params) - info->input_params = - realloc(info->input_params, - info->n_input_params * sizeof(struct spa_pod *)); - else { - free(info->input_params); - info->input_params = NULL; - } - for (i = 0; i < info->n_input_params; i++) { - info->input_params[i] = pw_spa_pod_copy(update->input_params[i]); - } - } 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 & PW_NODE_CHANGE_MASK_OUTPUT_PARAMS) { - for (i = 0; i < info->n_output_params; i++) - free(info->output_params[i]); - info->n_output_params = update->n_output_params; - if (info->n_output_params) - info->output_params = - realloc(info->output_params, - info->n_output_params * sizeof(struct spa_pod *)); - else { - free(info->output_params); - info->output_params = NULL; - } - for (i = 0; i < info->n_output_params; i++) { - info->output_params[i] = pw_spa_pod_copy(update->output_params[i]); - } - } if (update->change_mask & PW_NODE_CHANGE_MASK_STATE) { info->state = update->state; @@ -252,20 +219,9 @@ struct pw_node_info *pw_node_info_update(struct pw_node_info *info, void pw_node_info_free(struct pw_node_info *info) { - int i; if (info->name) free((void *) info->name); - if (info->input_params) { - for (i = 0; i < info->n_input_params; i++) - free(info->input_params[i]); - free(info->input_params); - } - if (info->output_params) { - for (i = 0; i < info->n_output_params; i++) - free(info->output_params[i]); - free(info->output_params); - } if (info->error) free((void *) info->error); if (info->props) @@ -273,6 +229,44 @@ void pw_node_info_free(struct pw_node_info *info) free(info); } +struct pw_port_info *pw_port_info_update(struct pw_port_info *info, + const struct pw_port_info *update) +{ + + if (update == NULL) + return info; + + if (info == NULL) { + info = calloc(1, sizeof(struct pw_port_info)); + if (info == NULL) + return NULL; + } + info->id = update->id; + info->change_mask = update->change_mask; + + if (update->change_mask & PW_PORT_CHANGE_MASK_NAME) { + if (info->name) + free((void *) info->name); + info->name = update->name ? strdup(update->name) : NULL; + } + if (update->change_mask & PW_PORT_CHANGE_MASK_PROPS) { + if (info->props) + pw_spa_dict_destroy(info->props); + info->props = pw_spa_dict_copy(update->props); + } + return info; +} + +void pw_port_info_free(struct pw_port_info *info) +{ + + if (info->name) + free((void *) info->name); + if (info->props) + pw_spa_dict_destroy(info->props); + free(info); +} + struct pw_factory_info *pw_factory_info_update(struct pw_factory_info *info, const struct pw_factory_info *update) { diff --git a/src/pipewire/introspect.h b/src/pipewire/introspect.h index bcbcfd87e..fd51d70e3 100644 --- a/src/pipewire/introspect.h +++ b/src/pipewire/introspect.h @@ -143,21 +143,16 @@ struct pw_node_info { uint32_t id; /**< id of the global */ #define PW_NODE_CHANGE_MASK_NAME (1 << 0) #define PW_NODE_CHANGE_MASK_INPUT_PORTS (1 << 1) -#define PW_NODE_CHANGE_MASK_INPUT_PARAMS (1 << 2) -#define PW_NODE_CHANGE_MASK_OUTPUT_PORTS (1 << 3) -#define PW_NODE_CHANGE_MASK_OUTPUT_PARAMS (1 << 4) -#define PW_NODE_CHANGE_MASK_STATE (1 << 5) -#define PW_NODE_CHANGE_MASK_PROPS (1 << 6) +#define PW_NODE_CHANGE_MASK_OUTPUT_PORTS (1 << 2) +#define PW_NODE_CHANGE_MASK_STATE (1 << 3) +#define PW_NODE_CHANGE_MASK_PROPS (1 << 4) +#define PW_NODE_CHANGE_MASK_ENUM_PARAMS (1 << 5) uint64_t change_mask; /**< bitfield of changed fields since last call */ 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 */ - struct spa_pod **input_params; /**< array of input params */ - uint32_t n_input_params; /**< number of input params */ uint32_t max_output_ports; /**< maximum number of outputs */ uint32_t n_output_ports; /**< number of outputs */ - struct spa_pod **output_params; /**< array of output params */ - uint32_t n_output_params; /**< number of output params */ enum pw_node_state state; /**< the current state of the node */ const char *error; /**< an error reason if \a state is error */ struct spa_dict *props; /**< the properties of the node */ @@ -172,8 +167,21 @@ pw_node_info_free(struct pw_node_info *info); struct pw_port_info { uint32_t id; /**< id of the global */ +#define PW_PORT_CHANGE_MASK_NAME (1 << 0) +#define PW_PORT_CHANGE_MASK_PROPS (1 << 1) +#define PW_PORT_CHANGE_MASK_ENUM_PARAMS (1 << 2) + uint64_t change_mask; /**< bitfield of changed fields since last call */ + const char *name; /**< name the port, suitable for display */ + struct spa_dict *props; /**< the properties of the port */ }; +struct pw_port_info * +pw_port_info_update(struct pw_port_info *info, + const struct pw_port_info *update); + +void +pw_port_info_free(struct pw_port_info *info); + /** The factory information. Extra information can be added in later versions \memberof pw_introspect */ struct pw_factory_info { uint32_t id; /**< id of the global */ diff --git a/src/pipewire/node.c b/src/pipewire/node.c index 2438921ed..f7403a95b 100644 --- a/src/pipewire/node.c +++ b/src/pipewire/node.c @@ -24,6 +24,7 @@ #include #include +#include #include "pipewire/pipewire.h" #include "pipewire/interfaces.h" @@ -43,6 +44,7 @@ struct impl { struct resource_data { struct spa_hook resource_listener; + struct pw_node *node; }; /** \endcond */ @@ -223,64 +225,11 @@ static int update_port_ids(struct pw_node *node) return 0; } -struct param_array { - struct spa_pod **params; - uint32_t n_params; -}; - -static int add_param(void *data, struct spa_pod *param) -{ - struct param_array *arr = data; - uint32_t idx = arr->n_params++; - - arr->params = realloc(arr->params, sizeof(struct spa_pod *) * arr->n_params); - arr->params[idx] = pw_spa_pod_copy(param); - return 0; -} - -static void -update_info(struct pw_node *this) -{ - struct pw_type *t = &this->core->type; - struct param_array params; - struct pw_port *port; - - params = (struct param_array) { NULL }; - if (!spa_list_is_empty(&this->input_ports)) { - port = spa_list_first(&this->input_ports, struct pw_port, link); - pw_port_for_each_param(port, t->param.idEnumFormat, NULL, add_param, ¶ms); - } - this->info.input_params = params.params; - this->info.n_input_params = params.n_params; - - params = (struct param_array) { NULL }; - if (!spa_list_is_empty(&this->output_ports)) { - port = spa_list_first(&this->output_ports, struct pw_port, link); - pw_port_for_each_param(port, t->param.idEnumFormat, NULL, add_param, ¶ms); - } - this->info.output_params = params.params; - this->info.n_output_params = params.n_params; -} - static void clear_info(struct pw_node *this) { - int i; - free((char*)this->info.name); - if (this->info.input_params) { - for (i = 0; i < this->info.n_input_params; i++) - free(this->info.input_params[i]); - free(this->info.input_params); - } - - if (this->info.output_params) { - for (i = 0; i < this->info.n_output_params; i++) - free(this->info.output_params[i]); - free(this->info.output_params); - } free((char*)this->info.error); - } static const struct pw_resource_events resource_events = { @@ -288,6 +237,28 @@ static const struct pw_resource_events resource_events = { .destroy = node_unbind_func, }; +static int reply_param(void *data, uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param) +{ + struct pw_resource *resource = data; + pw_node_resource_param(resource, id, index, next, param); + return 0; +} + +static void node_enum_params(void *object, uint32_t id, uint32_t index, uint32_t num, + const struct spa_pod *filter) +{ + struct pw_resource *resource = object; + struct resource_data *data = pw_resource_get_user_data(resource); + struct pw_node *node = data->node; + + pw_node_for_each_param(node, id, index, num, filter, reply_param, resource); +} + +static const struct pw_node_proxy_methods node_methods = { + PW_VERSION_NODE_PROXY_METHODS, + .enum_params = node_enum_params +}; + static void global_bind(void *_data, struct pw_client *client, uint32_t permissions, uint32_t version, uint32_t id) @@ -302,8 +273,11 @@ global_bind(void *_data, struct pw_client *client, uint32_t permissions, goto no_mem; data = pw_resource_get_user_data(resource); + data->node = this; pw_resource_add_listener(resource, &data->resource_listener, &resource_events, resource); + pw_resource_set_implementation(resource, &node_methods, resource); + pw_log_debug("node %p: bound to %d", this, resource->id); spa_list_append(&this->resource_list, &resource->link); @@ -362,7 +336,6 @@ int pw_node_register(struct pw_node *this, return -ENOMEM; update_port_ids(this); - update_info(this); pw_loop_invoke(this->data_loop, do_node_add, 1, NULL, 0, false, this); @@ -386,9 +359,11 @@ int pw_node_register(struct pw_node *this, this->info.id = this->global->id; spa_list_for_each(port, &this->input_ports, link) - pw_port_register(port, owner, this->global, pw_properties_copy(port->properties)); + pw_port_register(port, owner, this->global, + pw_properties_copy(port->properties)); spa_list_for_each(port, &this->output_ports, link) - pw_port_register(port, owner, this->global, pw_properties_copy(port->properties)); + pw_port_register(port, owner, this->global, + pw_properties_copy(port->properties)); spa_hook_list_call(&this->listener_list, struct pw_node_events, initialized); @@ -487,7 +462,7 @@ int pw_node_update_properties(struct pw_node *node, const struct spa_dict *dict) node->info.props = &node->properties->dict; - node->info.change_mask = PW_NODE_CHANGE_MASK_PROPS; + node->info.change_mask |= PW_NODE_CHANGE_MASK_PROPS; spa_hook_list_call(&node->listener_list, struct pw_node_events, info_changed, &node->info); @@ -504,7 +479,7 @@ static void node_done(void *data, int seq, int res) struct pw_node *node = data; struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this); - pw_log_debug("node %p: async complete event %d %d", node, seq, res); + pw_log_debug("node %p: async complete event %d %d %s", node, seq, res, spa_strerror(res)); pw_work_queue_complete(impl->work, node, seq, res); spa_hook_list_call(&node->listener_list, struct pw_node_events, async_complete, seq, res); } @@ -680,6 +655,39 @@ int pw_node_for_each_port(struct pw_node *node, return 0; } +int pw_node_for_each_param(struct pw_node *node, + uint32_t param_id, + uint32_t index, uint32_t max, + const struct spa_pod *filter, + int (*callback) (void *data, + uint32_t id, uint32_t index, uint32_t next, + struct spa_pod *param), + void *data) +{ + int res = 0; + uint32_t idx, count; + uint8_t buf[4096]; + struct spa_pod_builder b = { 0 }; + struct spa_pod *param; + + if (max == 0) + max = UINT32_MAX; + + for (count = 0; count < max; count++) { + spa_pod_builder_init(&b, buf, sizeof(buf)); + + idx = index; + if ((res = spa_node_enum_params(node->node, + param_id, &index, + filter, ¶m, &b)) <= 0) + break; + + if ((res = callback(data, param_id, idx, index, param)) != 0) + break; + } + return res; +} + struct pw_port * pw_node_find_port(struct pw_node *node, enum pw_direction direction, uint32_t port_id) { diff --git a/src/pipewire/node.h b/src/pipewire/node.h index 4bde868c2..dfa585b57 100644 --- a/src/pipewire/node.h +++ b/src/pipewire/node.h @@ -150,6 +150,14 @@ int pw_node_for_each_port(struct pw_node *node, int (*callback) (void *data, struct pw_port *port), void *data); +int pw_node_for_each_param(struct pw_node *node, + uint32_t param_id, + uint32_t index, uint32_t max, + const struct spa_pod *filter, + int (*callback) (void *data, + uint32_t id, uint32_t index, uint32_t next, + struct spa_pod *param), + void *data); /** Find the port with direction and port_id or NULL when not found */ struct pw_port * diff --git a/src/pipewire/port.c b/src/pipewire/port.c index 813358b35..4143e0ebb 100644 --- a/src/pipewire/port.c +++ b/src/pipewire/port.c @@ -21,6 +21,8 @@ #include #include +#include + #include "pipewire/pipewire.h" #include "pipewire/private.h" #include "pipewire/port.h" @@ -31,6 +33,12 @@ struct impl { struct spa_node mix_node; }; + +struct resource_data { + struct spa_hook resource_listener; + struct pw_port *port; +}; + /** \endcond */ @@ -128,7 +136,8 @@ static int schedule_mix_output(struct spa_node *data) *p->io = *io; } else { - io->status = SPA_STATUS_OK; + io->status = SPA_STATUS_HAVE_BUFFER; + io->buffer_id = SPA_ID_INVALID; } pw_log_trace("mix output %d %d", io->status, io->buffer_id); return io->status; @@ -187,10 +196,14 @@ struct pw_port *pw_port_new(enum pw_direction direction, if (user_data_size > 0) this->user_data = SPA_MEMBER(impl, sizeof(struct impl), void); + this->info.props = &this->properties->dict; + spa_list_init(&this->links); spa_list_init(&this->control_list[0]); spa_list_init(&this->control_list[1]); + spa_list_init(&this->resource_list); + spa_hook_list_init(&this->listener_list); spa_graph_port_init(&this->rt.port, @@ -235,12 +248,22 @@ const struct pw_properties *pw_port_get_properties(struct pw_port *port) int pw_port_update_properties(struct pw_port *port, const struct spa_dict *dict) { + struct pw_resource *resource; uint32_t i; + for (i = 0; i < dict->n_items; i++) pw_properties_set(port->properties, dict->items[i].key, dict->items[i].value); + port->info.props = &port->properties->dict; + + port->info.change_mask |= PW_PORT_CHANGE_MASK_PROPS; spa_hook_list_call(&port->listener_list, struct pw_port_events, - properties_changed, port->properties); + info_changed, &port->info); + + spa_list_for_each(resource, &port->resource_list, link) + pw_port_resource_info(resource, &port->info); + + port->info.change_mask = 0; return 0; } @@ -268,6 +291,7 @@ static int do_add_port(struct spa_loop *loop, { struct pw_port *this = user_data; + this->rt.port.flags = this->spa_info->flags; spa_graph_port_add(&this->node->rt.node, &this->rt.port); spa_graph_node_add(this->rt.graph, &this->rt.mix_node); spa_graph_port_add(&this->rt.mix_node, &this->rt.mix_port); @@ -276,7 +300,7 @@ static int do_add_port(struct spa_loop *loop, return 0; } -static int make_control(void *data, struct spa_pod *param) +static int make_control(void *data, uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param) { struct pw_port *port = data; struct pw_node *node = port->node; @@ -284,10 +308,72 @@ static int make_control(void *data, struct spa_pod *param) return 0; } +static void port_unbind_func(void *data) +{ + struct pw_resource *resource = data; + spa_list_remove(&resource->link); +} + +static const struct pw_resource_events resource_events = { + PW_VERSION_RESOURCE_EVENTS, + .destroy = port_unbind_func, +}; + +static int reply_param(void *data, uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param) +{ + struct pw_resource *resource = data; + pw_port_resource_param(resource, id, index, next, param); + return 0; +} + +static void port_enum_params(void *object, uint32_t id, uint32_t index, uint32_t num, + const struct spa_pod *filter) +{ + struct pw_resource *resource = object; + struct resource_data *data = pw_resource_get_user_data(resource); + struct pw_port *port = data->port; + + pw_port_for_each_param(port, id, index, num, filter, + reply_param, resource); +} + +static const struct pw_port_proxy_methods port_methods = { + PW_VERSION_NODE_PROXY_METHODS, + .enum_params = port_enum_params +}; + static void global_bind(void *_data, struct pw_client *client, uint32_t permissions, uint32_t version, uint32_t id) { + struct pw_port *this = _data; + struct pw_global *global = this->global; + struct pw_resource *resource; + struct resource_data *data; + + resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); + if (resource == NULL) + goto no_mem; + + data = pw_resource_get_user_data(resource); + data->port = this; + pw_resource_add_listener(resource, &data->resource_listener, &resource_events, resource); + + pw_resource_set_implementation(resource, &port_methods, resource); + + pw_log_debug("port %p: bound to %d", this, resource->id); + + spa_list_append(&this->resource_list, &resource->link); + + this->info.change_mask = ~0; + pw_port_resource_info(resource, &this->info); + this->info.change_mask = 0; + return; + + no_mem: + pw_log_error("can't create port resource"); + pw_core_resource_error(client->core_resource, + client->core_resource->id, -ENOMEM, "no memory"); return; } @@ -336,10 +422,10 @@ int pw_port_add(struct pw_port *port, struct pw_node *node) spa_node_port_get_info(node->node, port->direction, port_id, - &port->info); + &port->spa_info); - if (port->info->props) - pw_port_update_properties(port, port->info->props); + if (port->spa_info->props) + pw_port_update_properties(port, port->spa_info->props); dir = port->direction == PW_DIRECTION_INPUT ? "in" : "out"; @@ -348,12 +434,12 @@ int pw_port_add(struct pw_port *port, struct pw_node *node) } pw_properties_set(port->properties, "port.direction", dir); - if (SPA_FLAG_CHECK(port->info->flags, SPA_PORT_INFO_FLAG_PHYSICAL)) + if (SPA_FLAG_CHECK(port->spa_info->flags, SPA_PORT_INFO_FLAG_PHYSICAL)) pw_properties_set(port->properties, "port.physical", "1"); - if (SPA_FLAG_CHECK(port->info->flags, SPA_PORT_INFO_FLAG_TERMINAL)) + if (SPA_FLAG_CHECK(port->spa_info->flags, SPA_PORT_INFO_FLAG_TERMINAL)) pw_properties_set(port->properties, "port.terminal", "1"); - pw_log_debug("port %p: add to node %p %08x", port, node, port->info->flags); + pw_log_debug("port %p: add to node %p %08x", port, node, port->spa_info->flags); if (port->direction == PW_DIRECTION_INPUT) { spa_list_append(&node->input_ports, &port->link); pw_map_insert_at(&node->input_port_map, port_id, port); @@ -367,8 +453,8 @@ int pw_port_add(struct pw_port *port, struct pw_node *node) node->info.change_mask |= PW_NODE_CHANGE_MASK_OUTPUT_PORTS; } - pw_port_for_each_param(port, t->param_io.idPropsOut, NULL, make_control, port); - pw_port_for_each_param(port, t->param_io.idPropsIn, NULL, make_control, port); + pw_port_for_each_param(port, t->param_io.idPropsOut, 0, 0, NULL, make_control, port); + pw_port_for_each_param(port, t->param_io.idPropsIn, 0, 0, NULL, make_control, port); pw_log_debug("port %p: setting node io", port); spa_node_port_set_io(node->node, @@ -498,26 +584,33 @@ int pw_port_send_command(struct pw_port *port, bool block, const struct spa_comm int pw_port_for_each_param(struct pw_port *port, uint32_t param_id, + uint32_t index, uint32_t max, const struct spa_pod *filter, - int (*callback) (void *data, struct spa_pod *param), + int (*callback) (void *data, + uint32_t id, uint32_t index, uint32_t next, + struct spa_pod *param), void *data) { - int res; - uint8_t buffer[4096]; + int res = 0; + uint8_t buf[4096]; struct spa_pod_builder b = { 0 }; - uint32_t state; + uint32_t idx, count; + struct pw_node *node = port->node; struct spa_pod *param; - for (state = 0;;) { - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - if ((res = spa_node_port_enum_params(port->node->node, - port->direction, - port->port_id, - param_id, &state, + if (max == 0) + max = UINT32_MAX; + + for (count = 0; count < max; count++) { + spa_pod_builder_init(&b, buf, sizeof(buf)); + idx = index; + if ((res = spa_node_port_enum_params(node->node, + port->direction, port->port_id, + param_id, &index, filter, ¶m, &b)) <= 0) break; - if ((res = callback(data, param)) != 0) + if ((res = callback(data, param_id, idx, index, param)) != 0) break; } return res; @@ -528,33 +621,35 @@ struct param_filter { struct pw_port *out_port; uint32_t in_param_id; uint32_t out_param_id; - int (*callback) (void *data, struct spa_pod *param); + int (*callback) (void *data, uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param); void *data; uint32_t n_params; }; -static int do_filter(void *data, struct spa_pod *param) +static int do_filter(void *data, uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param) { struct param_filter *f = data; f->n_params++; - return pw_port_for_each_param(f->out_port, f->out_param_id, param, f->callback, f->data); + return pw_port_for_each_param(f->out_port, f->out_param_id, 0, 0, param, f->callback, f->data); } int pw_port_for_each_filtered_param(struct pw_port *in_port, struct pw_port *out_port, uint32_t in_param_id, uint32_t out_param_id, - int (*callback) (void *data, struct spa_pod *param), + int (*callback) (void *data, + uint32_t id, uint32_t index, uint32_t next, + struct spa_pod *param), void *data) { int res; struct param_filter filter = { in_port, out_port, in_param_id, out_param_id, callback, data, 0 }; - if ((res = pw_port_for_each_param(in_port, in_param_id, NULL, do_filter, &filter)) < 0) + if ((res = pw_port_for_each_param(in_port, in_param_id, 0, 0, NULL, do_filter, &filter)) < 0) return res; if (filter.n_params == 0) - res = do_filter(&filter, NULL); + res = do_filter(&filter, 0, 0, 0, NULL); return res; } @@ -632,8 +727,8 @@ int pw_port_alloc_buffers(struct pw_port *port, return -EIO; res = spa_node_port_alloc_buffers(node->node, port->direction, port->port_id, - params, n_params, - buffers, n_buffers); + params, n_params, + buffers, n_buffers); pw_log_debug("port %p: alloc %d buffers: %d (%s)", port, *n_buffers, res, spa_strerror(res)); if (port->allocated) { diff --git a/src/pipewire/port.h b/src/pipewire/port.h index 247744e41..4bcd5377e 100644 --- a/src/pipewire/port.h +++ b/src/pipewire/port.h @@ -67,6 +67,9 @@ struct pw_port_events { /** The port is freed */ void (*free) (void *data); + /** the port info changed */ + void (*info_changed) (void *data, struct pw_port_info *info); + /** a new link is added on this port */ void (*link_added) (void *data, struct pw_link *link); @@ -76,9 +79,6 @@ struct pw_port_events { /** the state of the port changed */ void (*state_changed) (void *data, enum pw_port_state state); - /** the properties of the port changed */ - void (*properties_changed) (void *data, const struct pw_properties *properties); - /** a control was added to the port */ void (*control_added) (void *data, struct pw_control *control); diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 39680ed10..fc631abc1 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -292,8 +292,12 @@ struct pw_port { enum pw_direction direction; /**< port direction */ uint32_t port_id; /**< port id */ - struct pw_properties *properties; - const struct spa_port_info *info; + const struct spa_port_info *spa_info; + + struct pw_properties *properties; /**< properties of the port */ + struct pw_port_info info; + + struct spa_list resource_list; /**< list of resources for this port */ enum pw_port_state state; /**< state of the port */ @@ -498,15 +502,20 @@ void pw_port_destroy(struct pw_port *port); * The function returns 0 on success or the error returned by the callback. */ int pw_port_for_each_param(struct pw_port *port, uint32_t param_id, + uint32_t index, uint32_t max, const struct spa_pod *filter, - int (*callback) (void *data, struct spa_pod *param), + int (*callback) (void *data, + uint32_t id, uint32_t index, uint32_t next, + struct spa_pod *param), void *data); int pw_port_for_each_filtered_param(struct pw_port *in_port, struct pw_port *out_port, uint32_t in_param_id, uint32_t out_param_id, - int (*callback) (void *data, struct spa_pod *param), + int (*callback) (void *data, + uint32_t id, uint32_t index, uint32_t next, + struct spa_pod *param), void *data); /** Set a param on a port \memberof pw_port */ diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c index 2605a8c5a..90f65920b 100644 --- a/src/tools/pipewire-cli.c +++ b/src/tools/pipewire-cli.c @@ -175,6 +175,8 @@ static bool do_create_node(struct data *data, const char *cmd, char *args, char static bool do_destroy(struct data *data, const char *cmd, char *args, char **error); static bool do_create_link(struct data *data, const char *cmd, char *args, char **error); static bool do_export_node(struct data *data, const char *cmd, char *args, char **error); +static bool do_node_params(struct data *data, const char *cmd, char *args, char **error); +static bool do_port_params(struct data *data, const char *cmd, char *args, char **error); static struct command command_list[] = { { "help", "Show this help", do_help }, @@ -190,6 +192,8 @@ static struct command command_list[] = { { "destroy", "Destroy a global object. ", do_destroy }, { "create-link", "Create a link between nodes. []", do_create_link }, { "export-node", "Export a local node to the current remote. [remote-var]", do_export_node }, + { "node-params", "Enumerate params of a node []", do_node_params }, + { "port-params", "Enumerate params of a port []", do_port_params }, }; static bool do_help(struct data *data, const char *cmd, char *args, char **error) @@ -532,34 +536,31 @@ static void info_module(struct proxy_data *pd) static void info_node(struct proxy_data *pd) { struct pw_node_info *info = pd->info; - struct pw_type *t = pd->rd->data->t; - int i; info_global(pd); fprintf(stdout, "%c\tname: \"%s\"\n", MARK_CHANGE(0), info->name); - fprintf(stdout, "%c\tinput ports: %u/%u\n", MARK_CHANGE(1), info->n_input_ports, info->max_input_ports); - fprintf(stdout, "%c\tinput params:\n", MARK_CHANGE(2)); - for (i = 0; i < info->n_input_params; i++) { - uint32_t flags = 0; - if (spa_pod_is_object_type(info->input_params[i], t->spa_format)) - flags |= SPA_DEBUG_FLAG_FORMAT; - spa_debug_pod(info->input_params[i], flags); - } - - fprintf(stdout, "%c\toutput ports: %u/%u\n", MARK_CHANGE(3), info->n_output_ports, info->max_output_ports); - fprintf(stdout, "%c\toutput params:\n", MARK_CHANGE(4)); - for (i = 0; i < info->n_output_params; i++) { - uint32_t flags = 0; - if (spa_pod_is_object_type(info->output_params[i], t->spa_format)) - flags |= SPA_DEBUG_FLAG_FORMAT; - spa_debug_pod(info->output_params[i], flags); - } - fprintf(stdout, "%c\tstate: \"%s\"", MARK_CHANGE(5), pw_node_state_as_string(info->state)); + fprintf(stdout, "%c\tinput ports: %u/%u\n", MARK_CHANGE(1), + info->n_input_ports, info->max_input_ports); + fprintf(stdout, "%c\toutput ports: %u/%u\n", MARK_CHANGE(2), + info->n_output_ports, info->max_output_ports); + fprintf(stdout, "%c\tstate: \"%s\"", MARK_CHANGE(3), pw_node_state_as_string(info->state)); if (info->state == PW_NODE_STATE_ERROR && info->error) fprintf(stdout, " \"%s\"\n", info->error); else fprintf(stdout, "\n"); - print_properties(info->props, MARK_CHANGE(6), true); + print_properties(info->props, MARK_CHANGE(4), true); + fprintf(stdout, "%c\tenum_params\n", MARK_CHANGE(5)); + info->change_mask = 0; +} + +static void info_port(struct proxy_data *pd) +{ + struct pw_port_info *info = pd->info; + + info_global(pd); + fprintf(stdout, "%c\tname: \"%s\"\n", MARK_CHANGE(0), info->name); + print_properties(info->props, MARK_CHANGE(1), true); + fprintf(stdout, "%c\tenum_params\n", MARK_CHANGE(2)); info->change_mask = 0; } @@ -659,9 +660,64 @@ static void node_event_info(void *object, struct pw_node_info *info) } } +static void node_event_param(void *object, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct proxy_data *data = object; + struct remote_data *rd = data->rd; + struct pw_type *t = rd->data->t; + uint32_t flags = 0; + + fprintf(stdout, "remote %d node %d param %d index %d\n", + rd->id, data->global->id, id, index); + + if (spa_pod_is_object_type(param, t->spa_format)) + flags |= SPA_DEBUG_FLAG_FORMAT; + spa_debug_pod(param, flags); +} + static const struct pw_node_proxy_events node_events = { PW_VERSION_NODE_PROXY_EVENTS, - .info = node_event_info + .info = node_event_info, + .param = node_event_param +}; + + +static void port_event_info(void *object, struct pw_port_info *info) +{ + struct proxy_data *pd = object; + struct remote_data *rd = pd->rd; + if (pd->info) + fprintf(stdout, "remote %d port %d changed\n", rd->id, info->id); + pd->info = pw_port_info_update(pd->info, info); + if (pd->global == NULL) + pd->global = pw_map_lookup(&rd->globals, info->id); + if (pd->global && pd->global->info_pending) { + info_port(pd); + pd->global->info_pending = false; + } +} + +static void port_event_param(void *object, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct proxy_data *data = object; + struct remote_data *rd = data->rd; + struct pw_type *t = rd->data->t; + uint32_t flags = 0; + + fprintf(stdout, "remote %d port %d param %d index %d\n", + rd->id, data->global->id, id, index); + + if (spa_pod_is_object_type(param, t->spa_format)) + flags |= SPA_DEBUG_FLAG_FORMAT; + spa_debug_pod(param, flags); +} + +static const struct pw_port_proxy_events port_events = { + PW_VERSION_PORT_PROXY_EVENTS, + .info = port_event_info, + .param = port_event_param }; static void factory_event_info(void *object, struct pw_factory_info *info) @@ -780,6 +836,12 @@ static bool bind_global(struct remote_data *rd, struct global *global, char **er destroy = (pw_destroy_t) pw_node_info_free; info_func = info_node; } + else if (global->type == t->port) { + events = &port_events; + client_version = PW_VERSION_PORT; + destroy = (pw_destroy_t) pw_port_info_free; + info_func = info_port; + } else if (global->type == t->factory) { events = &factory_events; client_version = PW_VERSION_FACTORY; @@ -1033,6 +1095,81 @@ static bool do_export_node(struct data *data, const char *cmd, char *args, char return false; } +static bool do_node_params(struct data *data, const char *cmd, char *args, char **error) +{ + struct pw_type *t = data->t; + struct remote_data *rd = data->current; + char *a[2]; + int n; + uint32_t id, param_id; + struct global *global; + + n = pw_split_ip(args, WHITESPACE, 2, a); + if (n < 1) { + asprintf(error, "%s []", cmd); + return false; + } + if (n == 2) + param_id = spa_type_map_get_id(t->map, a[1]); + else + param_id = t->param.idList; + + id = atoi(a[0]); + global = pw_map_lookup(&rd->globals, id); + if (global == NULL) { + asprintf(error, "%s: unknown global %d", cmd, id); + return false; + } + if (global->type != t->node) { + asprintf(error, "object %d is not a node", atoi(a[0])); + return false; + } + pw_node_proxy_enum_params((struct pw_node_proxy*)global->proxy, + param_id, 0, 0, NULL); + + return true; +} + +static bool do_port_params(struct data *data, const char *cmd, char *args, char **error) +{ + struct pw_type *t = data->t; + struct remote_data *rd = data->current; + char *a[2]; + int n; + uint32_t id, param_id; + struct global *global; + + n = pw_split_ip(args, WHITESPACE, 2, a); + if (n < 1) { + asprintf(error, "%s []", cmd); + return false; + } + if (n == 2) + param_id = spa_type_map_get_id(t->map, a[1]); + else + param_id = t->param.idList; + + id = atoi(a[0]); + global = pw_map_lookup(&rd->globals, id); + if (global == NULL) { + asprintf(error, "%s: unknown global %d", cmd, id); + return false; + } + if (global->type != t->port) { + asprintf(error, "object %d is not a port", atoi(a[0])); + return false; + } + if (global->proxy == NULL) { + if (!bind_global(rd, global, error)) + return false; + } + + pw_port_proxy_enum_params((struct pw_port_proxy*)global->proxy, + param_id, 0, 0, NULL); + + return true; +} + static bool parse(struct data *data, char *buf, size_t size, char **error) { char *a[2]; diff --git a/src/tools/pipewire-monitor.c b/src/tools/pipewire-monitor.c index b0a3addb5..f92f4e32d 100644 --- a/src/tools/pipewire-monitor.c +++ b/src/tools/pipewire-monitor.c @@ -26,6 +26,10 @@ #include #include +struct proxy_data; + +typedef void (*print_func_t) (struct proxy_data *data); + struct data { struct pw_main_loop *loop; struct pw_core *core; @@ -37,10 +41,14 @@ struct data { struct pw_registry_proxy *registry_proxy; struct spa_hook registry_listener; + + uint32_t seq; + struct spa_list pending_list; }; struct proxy_data { struct data *data; + bool first; struct pw_proxy *proxy; uint32_t id; uint32_t parent_id; @@ -51,8 +59,56 @@ struct proxy_data { pw_destroy_t destroy; struct spa_hook proxy_listener; struct spa_hook proxy_proxy_listener; + uint32_t pending_seq; + struct spa_list pending_link; + print_func_t print_func; + uint32_t n_params; + struct spa_pod **params; }; +static void add_pending(struct proxy_data *pd) +{ + struct data *d = pd->data; + + spa_list_append(&d->pending_list, &pd->pending_link); + pd->pending_seq = ++d->seq; + pw_core_proxy_sync(d->core_proxy, pd->pending_seq); +} + +static void on_sync_reply(void *data, uint32_t seq) +{ + struct data *d = data; + struct proxy_data *pd; + + spa_list_for_each(pd, &d->pending_list, pending_link) { + if (pd->pending_seq == seq) { + spa_list_remove(&pd->pending_link); + pd->pending_seq = SPA_ID_INVALID; + pd->print_func(pd); + } + } +} + +static void clear_params(struct proxy_data *data) +{ + int i; + + for (i = 0; i < data->n_params; i++) + free(data->params[i]); + free(data->params); + + data->n_params = 0; + data->params = NULL; +} + +static void add_param(struct proxy_data *data, const struct spa_pod *param) +{ + uint32_t idx = data->n_params++; + + data->params = realloc(data->params, sizeof(struct spa_pod *) * data->n_params); + data->params[idx] = pw_spa_pod_copy(param); +} + static void print_properties(const struct spa_dict *props, char mark) { const struct spa_dict_item *item; @@ -124,24 +180,23 @@ static const struct pw_module_proxy_events module_events = { .info = module_event_info, }; -static void node_event_info(void *object, struct pw_node_info *info) +static void print_node(struct proxy_data *data) { - struct proxy_data *data = object; + struct pw_node_info *info = data->info; bool print_all, print_mark; struct pw_type *t = pw_core_get_type(data->data->core); print_all = true; - if (data->info == NULL) { + if (data->first) { printf("added:\n"); print_mark = false; + data->first = false; } else { printf("changed:\n"); print_mark = true; } - info = data->info = pw_node_info_update(data->info, info); - printf("\tid: %d\n", data->id); printf("\tparent_id: %d\n", data->parent_id); printf("\tpermissions: %c%c%c\n", data->permissions & PW_PERM_R ? 'r' : '-', @@ -152,36 +207,122 @@ static void node_event_info(void *object, struct pw_node_info *info) int i; printf("%c\tname: \"%s\"\n", MARK_CHANGE(0), info->name); - printf("%c\tinput ports: %u/%u\n", MARK_CHANGE(1), info->n_input_ports, info->max_input_ports); - printf("%c\tinput params:\n", MARK_CHANGE(2)); - for (i = 0; i < info->n_input_params; i++) { + printf("%c\tparams:\n", MARK_CHANGE(5)); + for (i = 0; i < data->n_params; i++) { uint32_t flags = 0; - if (spa_pod_is_object_type(info->input_params[i], t->spa_format)) + if (spa_pod_is_object_type(data->params[i], t->spa_format)) flags |= SPA_DEBUG_FLAG_FORMAT; - spa_debug_pod(info->input_params[i], flags); + spa_debug_pod(data->params[i], flags); } - - printf("%c\toutput ports: %u/%u\n", MARK_CHANGE(3), info->n_output_ports, info->max_output_ports); - printf("%c\toutput params:\n", MARK_CHANGE(4)); - for (i = 0; i < info->n_output_params; i++) { - uint32_t flags = 0; - if (spa_pod_is_object_type(info->output_params[i], t->spa_format)) - flags |= SPA_DEBUG_FLAG_FORMAT; - spa_debug_pod(info->output_params[i], flags); - } - - printf("%c\tstate: \"%s\"", MARK_CHANGE(5), pw_node_state_as_string(info->state)); + printf("%c\tinput ports: %u/%u\n", MARK_CHANGE(1), + info->n_input_ports, info->max_input_ports); + printf("%c\toutput ports: %u/%u\n", MARK_CHANGE(2), + info->n_output_ports, info->max_output_ports); + printf("%c\tstate: \"%s\"", MARK_CHANGE(3), pw_node_state_as_string(info->state)); if (info->state == PW_NODE_STATE_ERROR && info->error) printf(" \"%s\"\n", info->error); else printf("\n"); - print_properties(info->props, MARK_CHANGE(6)); + print_properties(info->props, MARK_CHANGE(4)); + + } } +static void node_event_info(void *object, struct pw_node_info *info) +{ + struct proxy_data *data = object; + struct pw_type *t = pw_core_get_type(data->data->core); + + data->info = pw_node_info_update(data->info, info); + + if (info->change_mask & PW_NODE_CHANGE_MASK_ENUM_PARAMS) { + pw_node_proxy_enum_params((struct pw_node_proxy*)data->proxy, + t->param.idList, 0, 0, NULL); + add_pending(data); + } + if (data->pending_seq == SPA_ID_INVALID) + data->print_func(data); +} + +static void node_event_param(void *object, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct proxy_data *data = object; + add_param(data, param); +} + static const struct pw_node_proxy_events node_events = { PW_VERSION_NODE_PROXY_EVENTS, - .info = node_event_info + .info = node_event_info, + .param = node_event_param +}; + +static void print_port(struct proxy_data *data) +{ + struct pw_port_info *info = data->info; + bool print_all, print_mark; + struct pw_type *t = pw_core_get_type(data->data->core); + + print_all = true; + if (data->first) { + printf("added:\n"); + print_mark = false; + data->first = false; + } + else { + printf("changed:\n"); + print_mark = true; + } + + printf("\tid: %d\n", data->id); + printf("\tparent_id: %d\n", data->parent_id); + printf("\tpermissions: %c%c%c\n", data->permissions & PW_PERM_R ? 'r' : '-', + data->permissions & PW_PERM_W ? 'w' : '-', + data->permissions & PW_PERM_X ? 'x' : '-'); + printf("\ttype: %s (version %d)\n", PW_TYPE_INTERFACE__Port, data->version); + if (print_all) { + int i; + printf("%c\tname: \"%s\"\n", MARK_CHANGE(0), info->name); + printf("%c\tparams:\n", MARK_CHANGE(2)); + for (i = 0; i < data->n_params; i++) { + uint32_t flags = 0; + if (spa_pod_is_object_type(data->params[i], t->spa_format)) + flags |= SPA_DEBUG_FLAG_FORMAT; + spa_debug_pod(data->params[i], flags); + } + print_properties(info->props, MARK_CHANGE(1)); + } +} + +static void port_event_info(void *object, struct pw_port_info *info) +{ + + struct proxy_data *data = object; + struct pw_type *t = pw_core_get_type(data->data->core); + + data->info = pw_port_info_update(data->info, info); + + if (info->change_mask & PW_PORT_CHANGE_MASK_ENUM_PARAMS) { + pw_port_proxy_enum_params((struct pw_port_proxy*)data->proxy, + t->param.idEnumFormat, 0, 0, NULL); + add_pending(data); + } + if (data->pending_seq == SPA_ID_INVALID) + data->print_func(data); +} + +static void port_event_param(void *object, uint32_t id, uint32_t index, uint32_t next, + const struct spa_pod *param) +{ + struct proxy_data *data = object; + add_param(data, param); +} + +static const struct pw_port_proxy_events port_events = { + PW_VERSION_PORT_PROXY_EVENTS, + .info = port_event_info, + .param = port_event_param }; static void factory_event_info(void *object, struct pw_factory_info *info) @@ -300,6 +441,8 @@ destroy_proxy (void *data) { struct proxy_data *user_data = data; + clear_params(user_data); + if (user_data->info == NULL) return; @@ -325,11 +468,19 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, struct pw_type *t = pw_core_get_type(core); struct proxy_data *pd; pw_destroy_t destroy; + print_func_t print_func = NULL; if (type == t->node) { events = &node_events; client_version = PW_VERSION_NODE; destroy = (pw_destroy_t) pw_node_info_free; + print_func = print_node; + } + else if (type == t->port) { + events = &port_events; + client_version = PW_VERSION_PORT; + destroy = (pw_destroy_t) pw_port_info_free; + print_func = print_port; } else if (type == t->module) { events = &module_events; @@ -371,12 +522,15 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id, pd = pw_proxy_get_user_data(proxy); pd->data = d; + pd->first = true; pd->proxy = proxy; pd->id = id; pd->parent_id = parent_id; pd->permissions = permissions; pd->version = version; pd->destroy = destroy; + pd->pending_seq = SPA_ID_INVALID; + pd->print_func = print_func; pw_proxy_add_proxy_listener(proxy, &pd->proxy_proxy_listener, events, pd); pw_proxy_add_listener(proxy, &pd->proxy_listener, &proxy_events, pd); @@ -433,6 +587,7 @@ static const struct pw_remote_events remote_events = { PW_VERSION_REMOTE_EVENTS, .info_changed = on_info_changed, .state_changed = on_state_changed, + .sync_reply = on_sync_reply, }; static void do_quit(void *data, int signal_number) @@ -472,6 +627,9 @@ int main(int argc, char *argv[]) if (pw_remote_connect(data.remote) < 0) return -1; + data.seq = 1; + spa_list_init(&data.pending_list); + pw_main_loop_run(data.loop); pw_remote_destroy(data.remote);