mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	media-session: bluez-autoswitch: save user-selected headset profile
When switching back from the recording mode, save currently selected profile as the default headset profile to use in future. This allows users to select a specific profile they want to use for input, and to avoid autoswitching to HFP/HSP if they want to use a different microphone, without needing to edit configuration files.
This commit is contained in:
		
							parent
							
								
									ab5fcf4a66
								
							
						
					
					
						commit
						82fc54f252
					
				
					 1 changed files with 89 additions and 16 deletions
				
			
		| 
						 | 
					@ -71,7 +71,7 @@ struct impl {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	char *default_sink;
 | 
						char *default_sink;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct pw_properties *old_profiles;
 | 
						struct pw_properties *properties;
 | 
				
			||||||
	bool switched;
 | 
						bool switched;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -209,9 +209,46 @@ static int set_profile(struct sm_device *dev, const char *profile_name)
 | 
				
			||||||
				       SPA_PARAM_PROFILE_index,	  SPA_POD_Int(index)));
 | 
									       SPA_PARAM_PROFILE_index,	  SPA_POD_Int(index)));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool has_pending_restore(struct impl *impl)
 | 
					const char *get_saved_profile(struct impl *impl, const char *dev_name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return (impl->old_profiles->dict.n_items > 0);
 | 
						char saved_profile_key[512];
 | 
				
			||||||
 | 
						spa_scnprintf(saved_profile_key, sizeof(saved_profile_key), "%s:profile", dev_name);
 | 
				
			||||||
 | 
						return pw_properties_get(impl->properties, saved_profile_key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_saved_profile(struct impl *impl, const char *dev_name, const char *profile_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char saved_profile_key[512];
 | 
				
			||||||
 | 
						spa_scnprintf(saved_profile_key, sizeof(saved_profile_key), "%s:profile", dev_name);
 | 
				
			||||||
 | 
						pw_properties_set(impl->properties, saved_profile_key, profile_name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool get_pending_save(struct impl *impl, const char *dev_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char saved_profile_key[512];
 | 
				
			||||||
 | 
						spa_scnprintf(saved_profile_key, sizeof(saved_profile_key), "%s:pending-save", dev_name);
 | 
				
			||||||
 | 
						return spa_atob(pw_properties_get(impl->properties, saved_profile_key));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_pending_save(struct impl *impl, const char *dev_name, bool pending)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char saved_profile_key[512];
 | 
				
			||||||
 | 
						spa_scnprintf(saved_profile_key, sizeof(saved_profile_key), "%s:pending-save", dev_name);
 | 
				
			||||||
 | 
						pw_properties_set(impl->properties, saved_profile_key, pending ? "true" : NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *get_saved_headset_profile(struct impl *impl, const char *dev_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char saved_profile_key[512];
 | 
				
			||||||
 | 
						spa_scnprintf(saved_profile_key, sizeof(saved_profile_key), "%s:headset-profile", dev_name);
 | 
				
			||||||
 | 
						return pw_properties_get(impl->properties, saved_profile_key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void set_saved_headset_profile(struct impl *impl, const char *dev_name, const char *profile_name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char saved_profile_key[512];
 | 
				
			||||||
 | 
						spa_scnprintf(saved_profile_key, sizeof(saved_profile_key), "%s:headset-profile", dev_name);
 | 
				
			||||||
 | 
						pw_properties_set(impl->properties, saved_profile_key, profile_name);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int do_restore_profile(void *data, struct sm_object *obj)
 | 
					static int do_restore_profile(void *data, struct sm_object *obj)
 | 
				
			||||||
| 
						 | 
					@ -220,6 +257,7 @@ static int do_restore_profile(void *data, struct sm_object *obj)
 | 
				
			||||||
	struct sm_device *dev;
 | 
						struct sm_device *dev;
 | 
				
			||||||
	const char *dev_name;
 | 
						const char *dev_name;
 | 
				
			||||||
	const char *profile_name;
 | 
						const char *profile_name;
 | 
				
			||||||
 | 
						struct sm_param *p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Find old profile and restore it */
 | 
						/* Find old profile and restore it */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -227,15 +265,33 @@ static int do_restore_profile(void *data, struct sm_object *obj)
 | 
				
			||||||
		goto next;
 | 
							goto next;
 | 
				
			||||||
	if ((dev_name = pw_properties_get(obj->props, PW_KEY_DEVICE_NAME)) == NULL)
 | 
						if ((dev_name = pw_properties_get(obj->props, PW_KEY_DEVICE_NAME)) == NULL)
 | 
				
			||||||
		goto next;
 | 
							goto next;
 | 
				
			||||||
	if ((profile_name = pw_properties_get(impl->old_profiles, dev_name)) == NULL)
 | 
						if ((profile_name = get_saved_profile(impl, dev_name)) == NULL)
 | 
				
			||||||
		goto next;
 | 
							goto next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev = SPA_CONTAINER_OF(obj, struct sm_device, obj);
 | 
						dev = SPA_CONTAINER_OF(obj, struct sm_device, obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Save user-selected headset profile */
 | 
				
			||||||
 | 
						if (get_pending_save(impl, dev_name)) {
 | 
				
			||||||
 | 
							spa_list_for_each(p, &dev->param_list, link) {
 | 
				
			||||||
 | 
								const char *name;
 | 
				
			||||||
 | 
								if (p->id != SPA_PARAM_Profile || !p->param)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								if (spa_pod_parse_object(p->param,
 | 
				
			||||||
 | 
												SPA_TYPE_OBJECT_ParamProfile, NULL,
 | 
				
			||||||
 | 
												SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								set_saved_headset_profile(impl, dev_name, name);
 | 
				
			||||||
 | 
								set_pending_save(impl, dev_name, false);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Restore previous profile */
 | 
				
			||||||
	set_profile(dev, profile_name);
 | 
						set_profile(dev, profile_name);
 | 
				
			||||||
	pw_properties_set(impl->old_profiles, dev_name, NULL);
 | 
						set_saved_profile(impl, dev_name, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
next:
 | 
					next:
 | 
				
			||||||
	return has_pending_restore(impl) ? 0 : 1;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void remove_restore_timeout(struct impl *impl)
 | 
					static void remove_restore_timeout(struct impl *impl)
 | 
				
			||||||
| 
						 | 
					@ -262,7 +318,7 @@ static void restore_timeout(void *data, uint64_t expirations)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Restore previous profiles to devices */
 | 
						/* Restore previous profiles to devices */
 | 
				
			||||||
	sm_media_session_for_each_object(impl->session, do_restore_profile, impl);
 | 
						sm_media_session_for_each_object(impl->session, do_restore_profile, impl);
 | 
				
			||||||
	if ((res = sm_media_session_save_state(impl->session, SESSION_KEY, impl->old_profiles)) < 0)
 | 
						if ((res = sm_media_session_save_state(impl->session, SESSION_KEY, impl->properties)) < 0)
 | 
				
			||||||
		pw_log_error("can't save "SESSION_KEY" state: %s", spa_strerror(res));
 | 
							pw_log_error("can't save "SESSION_KEY" state: %s", spa_strerror(res));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	impl->switched = false;
 | 
						impl->switched = false;
 | 
				
			||||||
| 
						 | 
					@ -293,6 +349,7 @@ static void switch_profile_if_needed(struct impl *impl)
 | 
				
			||||||
	const char *headset_profile_name = NULL;
 | 
						const char *headset_profile_name = NULL;
 | 
				
			||||||
	enum spa_direction direction;
 | 
						enum spa_direction direction;
 | 
				
			||||||
	const char *dev_name;
 | 
						const char *dev_name;
 | 
				
			||||||
 | 
						const char *saved_headset_profile = NULL;
 | 
				
			||||||
	const char *str;
 | 
						const char *str;
 | 
				
			||||||
	int res;
 | 
						int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -326,11 +383,14 @@ static void switch_profile_if_needed(struct impl *impl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	remove_restore_timeout(impl);
 | 
						remove_restore_timeout(impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pw_properties_get(impl->old_profiles, dev_name)) {
 | 
						if (get_saved_profile(impl, dev_name)) {
 | 
				
			||||||
		/* We already switched this device */
 | 
							/* We already switched this device */
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Find saved headset profile, if any */
 | 
				
			||||||
 | 
						saved_headset_profile = get_saved_headset_profile(impl, dev_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Find current profile, and highest-priority profile with input route */
 | 
						/* Find current profile, and highest-priority profile with input route */
 | 
				
			||||||
	spa_list_for_each(p, &dev->param_list, link) {
 | 
						spa_list_for_each(p, &dev->param_list, link) {
 | 
				
			||||||
		const char *name;
 | 
							const char *name;
 | 
				
			||||||
| 
						 | 
					@ -374,12 +434,19 @@ static void switch_profile_if_needed(struct impl *impl)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		case SPA_PARAM_Profile:
 | 
							case SPA_PARAM_Profile:
 | 
				
			||||||
 | 
							case SPA_PARAM_EnumProfile:
 | 
				
			||||||
			if (spa_pod_parse_object(p->param,
 | 
								if (spa_pod_parse_object(p->param,
 | 
				
			||||||
							SPA_TYPE_OBJECT_ParamProfile, NULL,
 | 
												SPA_TYPE_OBJECT_ParamProfile, NULL,
 | 
				
			||||||
							SPA_PARAM_PROFILE_index, SPA_POD_Int(&idx),
 | 
												SPA_PARAM_PROFILE_index, SPA_POD_Int(&idx),
 | 
				
			||||||
							SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0)
 | 
												SPA_PARAM_PROFILE_name, SPA_POD_String(&name)) < 0)
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
 | 
								if (p->id == SPA_PARAM_Profile) {
 | 
				
			||||||
				current_profile_name = name;
 | 
									current_profile_name = name;
 | 
				
			||||||
 | 
								} else if (spa_streq(name, saved_headset_profile)) {
 | 
				
			||||||
 | 
									/* Saved headset profile takes priority */
 | 
				
			||||||
 | 
									headset_profile_priority = INT32_MAX;
 | 
				
			||||||
 | 
									headset_profile_name = name;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -387,9 +454,10 @@ static void switch_profile_if_needed(struct impl *impl)
 | 
				
			||||||
	if (set_profile(dev, headset_profile_name) < 0)
 | 
						if (set_profile(dev, headset_profile_name) < 0)
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_properties_set(impl->old_profiles, dev_name, current_profile_name);
 | 
						set_saved_profile(impl, dev_name, current_profile_name);
 | 
				
			||||||
 | 
						set_pending_save(impl, dev_name, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((res = sm_media_session_save_state(impl->session, SESSION_KEY, impl->old_profiles)) < 0)
 | 
						if ((res = sm_media_session_save_state(impl->session, SESSION_KEY, impl->properties)) < 0)
 | 
				
			||||||
		pw_log_error("can't save "SESSION_KEY" state: %s", spa_strerror(res));
 | 
							pw_log_error("can't save "SESSION_KEY" state: %s", spa_strerror(res));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	impl->switched = true;
 | 
						impl->switched = true;
 | 
				
			||||||
| 
						 | 
					@ -467,7 +535,12 @@ static void session_create(void *data, struct sm_object *object)
 | 
				
			||||||
	struct impl *impl = data;
 | 
						struct impl *impl = data;
 | 
				
			||||||
	struct node *node;
 | 
						struct node *node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (spa_streq(object->type, PW_TYPE_INTERFACE_Device) && has_pending_restore(impl)) {
 | 
						if (spa_streq(object->type, PW_TYPE_INTERFACE_Device) && object->props) {
 | 
				
			||||||
 | 
							const char *str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((str = pw_properties_get(object->props, PW_KEY_DEVICE_NAME)) != NULL)
 | 
				
			||||||
 | 
								set_pending_save(impl, str, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		impl->switched = true;
 | 
							impl->switched = true;
 | 
				
			||||||
		add_restore_timeout(impl);
 | 
							add_restore_timeout(impl);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -517,7 +590,7 @@ static void session_destroy(void *data)
 | 
				
			||||||
	spa_hook_remove(&impl->listener);
 | 
						spa_hook_remove(&impl->listener);
 | 
				
			||||||
	if (impl->session->metadata)
 | 
						if (impl->session->metadata)
 | 
				
			||||||
		spa_hook_remove(&impl->meta_listener);
 | 
							spa_hook_remove(&impl->meta_listener);
 | 
				
			||||||
	pw_properties_free(impl->old_profiles);
 | 
						pw_properties_free(impl->properties);
 | 
				
			||||||
	free(impl->default_sink);
 | 
						free(impl->default_sink);
 | 
				
			||||||
	free(impl);
 | 
						free(impl);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -590,13 +663,13 @@ int sm_bluez5_autoswitch_start(struct sm_media_session *session)
 | 
				
			||||||
	impl->session = session;
 | 
						impl->session = session;
 | 
				
			||||||
	impl->context = session->context;
 | 
						impl->context = session->context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	impl->old_profiles = pw_properties_new(NULL, NULL);
 | 
						impl->properties = pw_properties_new(NULL, NULL);
 | 
				
			||||||
	if (impl->old_profiles == NULL) {
 | 
						if (impl->properties == NULL) {
 | 
				
			||||||
		free(impl);
 | 
							free(impl);
 | 
				
			||||||
		return -ENOMEM;
 | 
							return -ENOMEM;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((res = sm_media_session_load_state(impl->session, SESSION_KEY, impl->old_profiles)) < 0)
 | 
						if ((res = sm_media_session_load_state(impl->session, SESSION_KEY, impl->properties)) < 0)
 | 
				
			||||||
		pw_log_info("can't load "SESSION_KEY" state: %s", spa_strerror(res));
 | 
							pw_log_info("can't load "SESSION_KEY" state: %s", spa_strerror(res));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sm_media_session_add_listener(impl->session, &impl->listener, &session_events, impl);
 | 
						sm_media_session_add_listener(impl->session, &impl->listener, &session_events, impl);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue