/* PipeWire * * Copyright © 2018 Wim Taymans * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "spa-monitor.h" #include "spa-device.h" struct monitor_object { uint32_t id; char *name; struct spa_list link; struct spa_handle *handle; uint32_t type; void *object; struct spa_hook object_listener; }; struct impl { struct pw_spa_monitor this; struct pw_core *core; struct pw_global *parent; struct spa_list item_list; }; static void device_free(void *data) { struct monitor_object *obj = data; spa_hook_remove(&obj->object_listener); spa_list_remove(&obj->link); pw_unload_spa_handle(obj->handle); free(obj); } static const struct pw_device_events device_events = { PW_VERSION_DEVICE_EVENTS, .free = device_free }; static struct monitor_object *add_object(struct pw_spa_monitor *this, uint32_t id, const struct spa_monitor_object_info *info, uint64_t now) { struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); struct pw_core *core = impl->core; int res; struct spa_handle *handle; struct monitor_object *obj; const char *name, *str, *lib; void *iface; struct pw_properties *props = NULL; const struct spa_support *support; uint32_t n_support; if (info->props) props = pw_properties_new_dict(info->props); else props = pw_properties_new(NULL, NULL); if ((name = pw_properties_get(props, PW_KEY_DEVICE_NAME)) == NULL) name = "unknown"; pw_log_debug("monitor %p: add: \"%s\" (%u)", this, name, id); if ((str = pw_properties_get(props, PW_KEY_DEVICE_FORM_FACTOR)) != NULL) if (strcmp(str, "internal") == 0) now = 0; if (now != 0 && pw_properties_get(props, PW_KEY_DEVICE_PLUGGED) == NULL) pw_properties_setf(props, PW_KEY_DEVICE_PLUGGED, "%"PRIu64, now); lib = pw_core_find_spa_lib(core, info->factory_name); if (lib == NULL) { pw_log_warn("monitor %p: unknown library for %s", this, info->factory_name); goto error_free_props; } support = pw_core_get_support(core, &n_support); handle = pw_load_spa_handle(lib, info->factory_name, &props->dict, n_support, support); if (handle == NULL) { pw_log_error("can't make factory instance: %m"); goto error_free_props; } if ((res = spa_handle_get_interface(handle, info->type, &iface)) < 0) { pw_log_error("can't get %d interface: %d", info->type, res); goto error_free_handle; } obj = calloc(1, sizeof(struct monitor_object)); obj->id = id; obj->name = strdup(name); obj->handle = handle; obj->type = info->type; switch (obj->type) { case SPA_TYPE_INTERFACE_Device: { struct pw_device *device; device = pw_spa_device_new(core, NULL, impl->parent, name, 0, iface, handle, props, 0); pw_device_add_listener(device, &obj->object_listener, &device_events, obj); obj->object = device; break; } default: pw_log_error("interface %d not implemented", obj->type); goto error_free_object; } spa_list_append(&impl->item_list, &obj->link); return obj; error_free_object: free(obj->name); free(obj); error_free_handle: pw_unload_spa_handle(handle); error_free_props: pw_properties_free(props); return NULL; } static struct monitor_object *find_object(struct pw_spa_monitor *this, uint32_t id) { struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); struct monitor_object *obj; spa_list_for_each(obj, &impl->item_list, link) { if (obj->id == id) { return obj; } } return NULL; } void destroy_object(struct monitor_object *obj) { switch (obj->type) { case SPA_TYPE_INTERFACE_Node: pw_node_destroy(obj->object); break; case SPA_TYPE_INTERFACE_Device: pw_device_destroy(obj->object); break; default: break; } } static void change_object(struct pw_spa_monitor *this, struct monitor_object *obj, const struct spa_monitor_object_info *info, uint64_t now) { pw_log_debug("monitor %p: change: \"%s\" (%u)", this, obj->name, obj->id); } static int on_monitor_object_info(void *data, uint32_t id, const struct spa_monitor_object_info *info) { struct impl *impl = data; struct pw_spa_monitor *this = &impl->this; struct timespec now; uint64_t now_nsec; struct monitor_object *obj; clock_gettime(CLOCK_MONOTONIC, &now); now_nsec = SPA_TIMESPEC_TO_NSEC(&now); obj = find_object(this, id); if (info == NULL) { if (obj == NULL) return -ENODEV; pw_log_debug("monitor %p: remove: (%s) %u", this, obj->name, id); destroy_object(obj); } else if (obj == NULL) { obj = add_object(this, id, info, now_nsec); if (obj == NULL) return -ENOMEM; } else { change_object(this, obj, info, now_nsec); } return 0; } static void update_monitor(struct pw_core *core, const char *name) { const char *monitors; struct spa_dict_item item; const struct pw_properties *props; struct spa_dict dict = SPA_DICT_INIT(&item, 1); props = pw_core_get_properties(core); if (props) monitors = pw_properties_get(props, "monitors"); else monitors = NULL; item.key = "monitors"; if (monitors == NULL) item.value = name; else asprintf((char **) &item.value, "%s,%s", monitors, name); pw_core_update_properties(core, &dict); if (monitors != NULL) free((void *) item.value); } static const struct spa_monitor_callbacks callbacks = { SPA_VERSION_MONITOR_CALLBACKS, .object_info = on_monitor_object_info, }; struct pw_spa_monitor *pw_spa_monitor_load(struct pw_core *core, struct pw_global *parent, const char *factory_name, const char *system_name, struct pw_properties *properties, size_t user_data_size) { struct impl *impl; struct pw_spa_monitor *this; struct spa_handle *handle; const char *lib = NULL; int res; void *iface; const struct spa_support *support; uint32_t n_support; if (lib == NULL && properties) lib = pw_properties_get(properties, "spa.library.name"); if (lib == NULL) lib = pw_core_find_spa_lib(core, factory_name); if (lib == NULL) goto exit; support = pw_core_get_support(core, &n_support); handle = pw_load_spa_handle(lib, factory_name, properties ? &properties->dict : NULL, n_support, support); if (handle == NULL) goto exit; if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Monitor, &iface)) < 0) { pw_log_error("can't get MONITOR interface: %d", res); goto exit_unload; } impl = calloc(1, sizeof(struct impl) + user_data_size); if (impl == NULL) goto exit_unload; impl->core = core; impl->parent = parent; spa_list_init(&impl->item_list); this = &impl->this; this->monitor = iface; this->factory_name = strdup(factory_name); this->system_name = strdup(system_name); this->handle = handle; this->properties = properties; if (user_data_size > 0) this->user_data = SPA_MEMBER(impl, sizeof(struct impl), void); update_monitor(core, this->system_name); spa_monitor_set_callbacks(this->monitor, &callbacks, impl); return this; exit_unload: pw_unload_spa_handle(handle); exit: return NULL; } void pw_spa_monitor_destroy(struct pw_spa_monitor *monitor) { struct impl *impl = SPA_CONTAINER_OF(monitor, struct impl, this); struct monitor_object *obj, *tmp; pw_log_debug("spa-monitor %p: destroy", impl); spa_list_for_each_safe(obj, tmp, &impl->item_list, link) destroy_object(obj); pw_unload_spa_handle(monitor->handle); free(monitor->factory_name); free(monitor->system_name); if (monitor->properties) pw_properties_free(monitor->properties); free(impl); }