diff --git a/src/daemon/media-session.d/alsa-monitor.conf b/src/daemon/media-session.d/alsa-monitor.conf index 47c58e951..a1d991f22 100644 --- a/src/daemon/media-session.d/alsa-monitor.conf +++ b/src/daemon/media-session.d/alsa-monitor.conf @@ -24,8 +24,8 @@ rules = [ #api.alsa.ignore-dB = false #device.profile-set = "profileset-name" #device.profile = "default profile name" + api.acp.auto-profile = false #api.acp.auto-port = true - #api.acp.auto-profile = true #device.nick = "My Device" } } diff --git a/src/examples/media-session/default-profile.c b/src/examples/media-session/default-profile.c index 270152a6c..2218fdcbe 100644 --- a/src/examples/media-session/default-profile.c +++ b/src/examples/media-session/default-profile.c @@ -118,38 +118,99 @@ static void add_idle_timeout(struct impl *impl) pw_loop_update_timer(main_loop, impl->idle_timeout, &value, NULL, false); } -static uint32_t find_profile_id(struct device *dev, const char *name) +struct profile { + struct sm_param *p; + uint32_t index; + const char *name; + uint32_t prio; + uint32_t available; +}; + +static int parse_profile(struct sm_param *p, struct profile *pr) +{ + pr->p = p; + pr->prio = 0; + pr->available = SPA_PARAM_AVAILABILITY_unknown; + return spa_pod_parse_object(p->param, + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_index, SPA_POD_Int(&pr->index), + SPA_PARAM_PROFILE_name, SPA_POD_String(&pr->name), + SPA_PARAM_PROFILE_priority, SPA_POD_OPT_Int(&pr->prio), + SPA_PARAM_PROFILE_available, SPA_POD_OPT_Id(&pr->available)); +} + +static int find_profile(struct device *dev, uint32_t index, struct profile *pr) { struct sm_param *p; spa_list_for_each(p, &dev->obj->param_list, link) { - const char *n; - uint32_t id, available = SPA_PARAM_AVAILABILITY_unknown; - if (p->id != SPA_PARAM_EnumProfile || - spa_pod_parse_object(p->param, - SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_index, SPA_POD_Int(&id), - SPA_PARAM_PROFILE_name, SPA_POD_String(&n), - SPA_PARAM_PROFILE_available, SPA_POD_OPT_Id(&available)) < 0) { + parse_profile(p, pr) < 0) continue; - } - if (available == SPA_PARAM_AVAILABILITY_no) - continue; - if (strcmp(n, name) == 0) - return id; + if (index == pr->index) + return 0; } - return SPA_ID_INVALID; + return -ENOENT; } -static int restore_profile(struct device *dev) +static int find_current_profile(struct device *dev, struct profile *pr) +{ + struct sm_param *p; + spa_list_for_each(p, &dev->obj->param_list, link) { + if (p->id == SPA_PARAM_Profile && + parse_profile(p, pr) >= 0) + return 0; + } + return -ENOENT; +} + +static int find_best_profile(struct device *dev, struct profile *pr) +{ + struct sm_param *p; + struct profile best, best_avail, best_unk, off; + + spa_zero(best); + spa_zero(best_avail); + spa_zero(best_unk); + spa_zero(off); + + spa_list_for_each(p, &dev->obj->param_list, link) { + struct profile t; + + if (p->id != SPA_PARAM_EnumProfile || + parse_profile(p, &t) < 0) + continue; + + if (t.name && strcmp(t.name, "off") == 0) { + off = t; + } + else if (t.available == SPA_PARAM_AVAILABILITY_yes) { + if (best_avail.name == NULL || t.prio > best_avail.prio) + best_avail = t; + } + else if (t.available != SPA_PARAM_AVAILABILITY_no) { + if (best_unk.name == NULL || t.prio > best_unk.prio) + best_unk = t; + } + } + best = best_avail; + if (best.name == NULL) + best = best_unk; + if (best.name == NULL) + best = off; + if (best.name == NULL) + return -ENOENT; + *pr = best; + return 0; +} + +static int find_saved_profile(struct device *dev, struct profile *pr) { struct spa_json it[2]; struct impl *impl = dev->impl; const char *json, *value; int len; - uint32_t index = SPA_ID_INVALID; - char buf[1024], name[1024] = "\0"; - struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + char name[1024] = "\0"; + struct sm_param *p; json = pw_properties_get(impl->properties, dev->key); if (json == NULL) @@ -169,44 +230,71 @@ static int restore_profile(struct device *dev) } } pw_log_debug("device %d: find profile '%s'", dev->id, name); - index = find_profile_id(dev, name); - if (index == SPA_ID_INVALID) - return -ENOENT; - pw_log_info("device %d: restore profile '%s' index %d", dev->id, name, index); + spa_list_for_each(p, &dev->obj->param_list, link) { + if (p->id != SPA_PARAM_EnumProfile || + parse_profile(p, pr) < 0) + continue; + + if (strcmp(pr->name, name) == 0) + return 0; + } + return -ENOENT; +} + +static int set_profile(struct device *dev, struct profile *pr) +{ + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + + pw_log_info("device %d: restore profile '%s' index %d", + dev->id, pr->name, pr->index); + pw_device_set_param((struct pw_device*)dev->obj->obj.proxy, SPA_PARAM_Profile, 0, spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, - SPA_PARAM_PROFILE_index, SPA_POD_Int(index))); - dev->active_profile = index; + SPA_PARAM_PROFILE_index, SPA_POD_Int(pr->index))); + + dev->active_profile = pr->index; + return 0; } -static int handle_profile(struct device *dev, struct sm_param *p) +static int handle_profile(struct device *dev) { struct impl *impl = dev->impl; - uint32_t index; + struct profile pr; int res; + if (dev->active_profile != SPA_ID_INVALID) { + /* we had a profile but it is now gone or not available, + * try to restore a new profile */ + res = find_profile(dev, dev->active_profile, &pr); + if (res < 0 || pr.available == SPA_PARAM_AVAILABILITY_no) + dev->restored = false; + } if (!dev->restored) { - restore_profile(dev); - dev->restored = true; - } else { - const char *name; - if ((res = spa_pod_parse_object(p->param, - SPA_TYPE_OBJECT_ParamProfile, NULL, - SPA_PARAM_PROFILE_index, SPA_POD_Int(&index), - SPA_PARAM_PROFILE_name, SPA_POD_String(&name))) < 0) { - pw_log_warn("device %d: can't parse profile: %s", dev->id, spa_strerror(res)); - return res; + /* first try to restore our saved profile */ + res = find_saved_profile(dev, &pr); + if (res < 0 || pr.available == SPA_PARAM_AVAILABILITY_no) + /* it's not found or not available, try to find + * the next best profile */ + res = find_best_profile(dev, &pr); + if (res >= 0) { + if (set_profile(dev, &pr) >= 0) + dev->restored = true; } - if (dev->active_profile == index) + } else { + if ((res = find_current_profile(dev, &pr)) < 0) + return res; + + if (dev->active_profile == pr.index) return 0; - dev->active_profile = index; - pw_log_debug("device %d: current profile %d %s", dev->id, index, name); - pw_properties_setf(impl->properties, dev->key, "{ \"name\": \"%s\" }", name); + dev->active_profile = pr.index; + pw_log_info("device %d: current profile %d %s", dev->id, pr.index, pr.name); + pw_properties_setf(impl->properties, dev->key, "{ \"name\": \"%s\" }", pr.name); add_idle_timeout(impl); } return 0; @@ -226,23 +314,8 @@ static void object_update(void *data) strcmp(str, "bluetooth") == 0) return; - if (dev->obj->obj.changed & SM_DEVICE_CHANGE_MASK_PARAMS) { - struct sm_param *p; - spa_list_for_each(p, &dev->obj->param_list, link) { - if (pw_log_level_enabled(SPA_LOG_LEVEL_DEBUG)) - spa_debug_pod(2, NULL, p->param); - - switch (p->id) { - case SPA_PARAM_EnumProfile: - break; - case SPA_PARAM_Profile: - handle_profile(dev, p); - break; - default: - break; - } - } - } + if (dev->obj->obj.changed & SM_DEVICE_CHANGE_MASK_PARAMS) + handle_profile(dev); } static const struct sm_object_events object_events = {