mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-10-29 05:40:27 -04:00 
			
		
		
		
	Cache node, port and device params
So that we don't need to keep asking when it didn't change. Fixes #455
This commit is contained in:
		
							parent
							
								
									ec77979890
								
							
						
					
					
						commit
						ccfe439786
					
				
					 5 changed files with 366 additions and 46 deletions
				
			
		|  | @ -26,6 +26,7 @@ | |||
| 
 | ||||
| #include <spa/debug/types.h> | ||||
| #include <spa/monitor/utils.h> | ||||
| #include <spa/pod/filter.h> | ||||
| 
 | ||||
| #include "pipewire/impl.h" | ||||
| #include "pipewire/private.h" | ||||
|  | @ -34,6 +35,11 @@ | |||
| 
 | ||||
| struct impl { | ||||
| 	struct pw_impl_device this; | ||||
| 
 | ||||
| 	struct spa_list param_list; | ||||
| 	struct spa_list pending_list; | ||||
| 
 | ||||
| 	unsigned int cache_params:1; | ||||
| }; | ||||
| 
 | ||||
| #define pw_device_resource(r,m,v,...)	pw_resource_call(r,struct pw_device_events,m,v,__VA_ARGS__) | ||||
|  | @ -41,10 +47,13 @@ struct impl { | |||
| #define pw_device_resource_param(r,...) pw_device_resource(r,param,0,__VA_ARGS__) | ||||
| 
 | ||||
| struct result_device_params_data { | ||||
| 	struct impl *impl; | ||||
| 	void *data; | ||||
| 	int (*callback) (void *data, int seq, | ||||
| 			uint32_t id, uint32_t index, uint32_t next, | ||||
| 			struct spa_pod *param); | ||||
| 	int seq; | ||||
| 	unsigned int cache:1; | ||||
| }; | ||||
| 
 | ||||
| struct resource_data { | ||||
|  | @ -61,6 +70,7 @@ struct resource_data { | |||
| 	int seq; | ||||
| 	int orig_seq; | ||||
| 	int end; | ||||
| 	struct spa_param_info *pi; | ||||
| 	struct result_device_params_data data; | ||||
| 	struct spa_hook listener; | ||||
| }; | ||||
|  | @ -139,6 +149,9 @@ struct pw_impl_device *pw_context_create_device(struct pw_context *context, | |||
| 		res = -errno; | ||||
| 		goto error_cleanup; | ||||
| 	} | ||||
| 	spa_list_init(&impl->param_list); | ||||
| 	spa_list_init(&impl->pending_list); | ||||
| 	impl->cache_params = true; | ||||
| 
 | ||||
| 	this = &impl->this; | ||||
| 	this->name = strdup("device"); | ||||
|  | @ -179,6 +192,7 @@ error_cleanup: | |||
| SPA_EXPORT | ||||
| void pw_impl_device_destroy(struct pw_impl_device *device) | ||||
| { | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this); | ||||
| 	struct object_data *od; | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: destroy", device); | ||||
|  | @ -200,6 +214,9 @@ void pw_impl_device_destroy(struct pw_impl_device *device) | |||
| 	pw_log_debug(NAME" %p: free", device); | ||||
| 	pw_impl_device_emit_free(device); | ||||
| 
 | ||||
| 	pw_param_clear(&impl->param_list, SPA_ID_INVALID); | ||||
| 	pw_param_clear(&impl->pending_list, SPA_ID_INVALID); | ||||
| 
 | ||||
| 	spa_hook_list_clean(&device->listener_list); | ||||
| 
 | ||||
| 	pw_properties_free(device->properties); | ||||
|  | @ -210,7 +227,15 @@ void pw_impl_device_destroy(struct pw_impl_device *device) | |||
| 
 | ||||
| static void remove_busy_resource(struct resource_data *d) | ||||
| { | ||||
| 	struct pw_impl_device *device = d->device; | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this); | ||||
| 
 | ||||
| 	if (d->end != -1) { | ||||
| 		if (d->pi && d->data.cache) { | ||||
| 			pw_param_update(&impl->param_list, &impl->pending_list); | ||||
| 			d->pi->user = 1; | ||||
| 			d->pi = NULL; | ||||
| 		} | ||||
| 		spa_hook_remove(&d->listener); | ||||
| 		d->end = -1; | ||||
| 		pw_impl_client_set_busy(d->resource->client, false); | ||||
|  | @ -242,11 +267,18 @@ static const struct pw_resource_events resource_events = { | |||
| static void result_device_params(void *data, int seq, int res, uint32_t type, const void *result) | ||||
| { | ||||
| 	struct result_device_params_data *d = data; | ||||
| 	struct impl *impl = d->impl; | ||||
| 	pw_log_debug(NAME" %p: type %d", impl, type); | ||||
| 
 | ||||
| 	switch (type) { | ||||
| 	case SPA_RESULT_TYPE_DEVICE_PARAMS: | ||||
| 	{ | ||||
| 		const struct spa_result_device_params *r = result; | ||||
| 		d->callback(d->data, seq, r->id, r->index, r->next, r->param); | ||||
| 		if (d->cache) { | ||||
| 			pw_log_debug(NAME" %p: add param %d", impl, r->id); | ||||
| 			pw_param_add(&impl->pending_list, r->id, r->param); | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 	default: | ||||
|  | @ -265,26 +297,73 @@ int pw_impl_device_for_each_param(struct pw_impl_device *device, | |||
| 			     void *data) | ||||
| { | ||||
| 	int res; | ||||
| 	struct result_device_params_data user_data = { data, callback }; | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this); | ||||
| 	struct result_device_params_data user_data = { impl, data, callback, seq, false }; | ||||
| 	struct spa_hook listener; | ||||
| 	struct spa_param_info *pi; | ||||
| 	static const struct spa_device_events device_events = { | ||||
| 		SPA_VERSION_DEVICE_EVENTS, | ||||
| 		.result = result_device_params, | ||||
| 	}; | ||||
| 
 | ||||
| 	pi = pw_param_info_find(device->info.params, device->info.n_params, param_id); | ||||
| 	if (pi == NULL) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	if (max == 0) | ||||
| 		max = UINT32_MAX; | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u", device, param_id, | ||||
| 	pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u cached:%d", device, param_id, | ||||
| 			spa_debug_type_find_name(spa_type_param, param_id), | ||||
| 			index, max); | ||||
| 			index, max, pi->user); | ||||
| 
 | ||||
| 	spa_zero(listener); | ||||
| 	spa_device_add_listener(device->device, &listener, | ||||
| 			&device_events, &user_data); | ||||
| 	res = spa_device_enum_params(device->device, seq, | ||||
| 			param_id, index, max, filter); | ||||
| 	spa_hook_remove(&listener); | ||||
| 	if (pi->user == 1) { | ||||
| 		struct pw_param *p; | ||||
| 		uint8_t buffer[1024]; | ||||
| 		struct spa_pod_builder b = { 0 }; | ||||
| 	        struct spa_result_device_params result; | ||||
| 		uint32_t count = 0; | ||||
| 		bool found = false; | ||||
| 
 | ||||
| 		result.id = param_id; | ||||
| 		result.next = 0; | ||||
| 
 | ||||
| 		spa_list_for_each(p, &impl->param_list, link) { | ||||
| 			result.index = result.next++; | ||||
| 			if (p->id != param_id) | ||||
| 				continue; | ||||
| 
 | ||||
| 			found = true; | ||||
| 
 | ||||
| 			if (result.index < index) | ||||
| 				continue; | ||||
| 
 | ||||
| 			spa_pod_builder_init(&b, buffer, sizeof(buffer)); | ||||
| 			if (spa_pod_filter(&b, &result.param, p->param, filter) != 0) | ||||
| 				continue; | ||||
| 
 | ||||
| 			pw_log_debug(NAME " %p: %d param %u", device, seq, result.index); | ||||
| 			result_device_params(&user_data, seq, 0, SPA_RESULT_TYPE_DEVICE_PARAMS, &result); | ||||
| 
 | ||||
| 			if (++count == max) | ||||
| 				break; | ||||
| 		} | ||||
| 		res = found ? 0 : -ENOENT; | ||||
| 	} else { | ||||
| 		user_data.cache = impl->cache_params && filter == NULL; | ||||
| 
 | ||||
| 		spa_zero(listener); | ||||
| 		spa_device_add_listener(device->device, &listener, | ||||
| 				&device_events, &user_data); | ||||
| 		res = spa_device_enum_params(device->device, seq, | ||||
| 				param_id, index, max, filter); | ||||
| 		spa_hook_remove(&listener); | ||||
| 
 | ||||
| 		if (!SPA_RESULT_IS_ASYNC(res) && user_data.cache) { | ||||
| 			pw_param_update(&impl->param_list, &impl->pending_list); | ||||
| 			pi->user = 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return res; | ||||
| } | ||||
|  | @ -304,11 +383,10 @@ static void result_device_params_async(void *data, int seq, int res, uint32_t ty | |||
| 	pw_log_debug(NAME" %p: async result %d %d (%d/%d)", d->device, | ||||
| 			res, seq, d->seq, d->end); | ||||
| 
 | ||||
| 	if (seq == d->end) | ||||
| 		remove_busy_resource(d); | ||||
| 	if (seq == d->seq) | ||||
| 		result_device_params(&d->data, d->orig_seq, res, type, result); | ||||
| 
 | ||||
| 	if (seq == d->end) | ||||
| 		remove_busy_resource(d); | ||||
| } | ||||
| 
 | ||||
| static int device_enum_params(void *object, int seq, uint32_t id, uint32_t start, uint32_t num, | ||||
|  | @ -317,6 +395,7 @@ static int device_enum_params(void *object, int seq, uint32_t id, uint32_t start | |||
| 	struct resource_data *data = object; | ||||
| 	struct pw_resource *resource = data->resource; | ||||
| 	struct pw_impl_device *device = data->device; | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this); | ||||
| 	struct pw_impl_client *client = resource->client; | ||||
| 	int res; | ||||
| 	static const struct spa_device_events device_events = { | ||||
|  | @ -333,11 +412,15 @@ static int device_enum_params(void *object, int seq, uint32_t id, uint32_t start | |||
| 				spa_debug_type_find_name(spa_type_param, id)); | ||||
| 	} else if (SPA_RESULT_IS_ASYNC(res)) { | ||||
| 		pw_impl_client_set_busy(client, true); | ||||
| 		data->data.impl = impl; | ||||
| 		data->data.data = data; | ||||
| 		data->data.callback = reply_param; | ||||
| 		data->data.cache = impl->cache_params && filter == NULL; | ||||
| 		if (data->end == -1) | ||||
| 			spa_device_add_listener(device->device, &data->listener, | ||||
| 				&device_events, data); | ||||
| 		data->pi = pw_param_info_find(device->info.params, | ||||
| 				device->info.n_params, id); | ||||
| 		data->orig_seq = seq; | ||||
| 		data->seq = res; | ||||
| 		data->end = spa_device_sync(device->device, res); | ||||
|  | @ -634,8 +717,12 @@ static void emit_params(struct pw_impl_device *device, uint32_t *changed_ids, ui | |||
| static void device_info(void *data, const struct spa_device_info *info) | ||||
| { | ||||
| 	struct pw_impl_device *device = data; | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(device, struct impl, this); | ||||
| 	uint32_t changed_ids[MAX_PARAMS], n_changed_ids = 0; | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: flags:%08"PRIx64" change_mask:%08"PRIx64, | ||||
| 			device, info->flags, info->change_mask); | ||||
| 
 | ||||
| 	if (info->change_mask & SPA_DEVICE_CHANGE_MASK_PROPS) { | ||||
| 		update_properties(device, info->props); | ||||
| 	} | ||||
|  | @ -646,16 +733,22 @@ static void device_info(void *data, const struct spa_device_info *info) | |||
| 		device->info.n_params = SPA_MIN(info->n_params, SPA_N_ELEMENTS(device->params)); | ||||
| 
 | ||||
| 		for (i = 0; i < device->info.n_params; i++) { | ||||
| 			uint32_t id = info->params[i].id; | ||||
| 
 | ||||
| 			pw_log_debug(NAME" %p: param %d id:%d (%s) %08x:%08x", device, i, | ||||
|                                         info->params[i].id, | ||||
|                                         spa_debug_type_find_name(spa_type_param, info->params[i].id), | ||||
|                                         id, spa_debug_type_find_name(spa_type_param, id), | ||||
|                                         device->info.params[i].flags, info->params[i].flags); | ||||
| 
 | ||||
|                         if (device->info.params[i].flags != info->params[i].flags && | ||||
|                             info->params[i].flags & SPA_PARAM_INFO_READ) | ||||
|                                 changed_ids[n_changed_ids++] = info->params[i].id; | ||||
|                         if (device->info.params[i].flags == info->params[i].flags) | ||||
| 				continue; | ||||
| 
 | ||||
| 			pw_log_debug(NAME" %p: update param %d", device, id); | ||||
| 			pw_param_clear(&impl->pending_list, id); | ||||
|                         device->info.params[i] = info->params[i]; | ||||
|                         device->info.params[i].user = 0; | ||||
| 
 | ||||
| 			if (info->params[i].flags & SPA_PARAM_INFO_READ) | ||||
|                                 changed_ids[n_changed_ids++] = id; | ||||
|                 } | ||||
| 	} | ||||
| 	emit_info_changed(device); | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ | |||
| 
 | ||||
| #include <spa/support/system.h> | ||||
| #include <spa/pod/parser.h> | ||||
| #include <spa/pod/filter.h> | ||||
| #include <spa/node/utils.h> | ||||
| #include <spa/debug/types.h> | ||||
| 
 | ||||
|  | @ -52,7 +53,11 @@ struct impl { | |||
| 
 | ||||
| 	int last_error; | ||||
| 
 | ||||
| 	struct spa_list param_list; | ||||
| 	struct spa_list pending_list; | ||||
| 
 | ||||
| 	unsigned int pause_on_idle:1; | ||||
| 	unsigned int cache_params:1; | ||||
| }; | ||||
| 
 | ||||
| #define pw_node_resource(r,m,v,...)	pw_resource_call(r,struct pw_node_events,m,v,__VA_ARGS__) | ||||
|  | @ -814,6 +819,11 @@ static void check_properties(struct pw_impl_node *node) | |||
| 	else | ||||
| 		impl->pause_on_idle = true; | ||||
| 
 | ||||
| 	if ((str = pw_properties_get(node->properties, PW_KEY_NODE_CACHE_PARAMS))) | ||||
| 		impl->cache_params = pw_properties_parse_bool(str); | ||||
| 	else | ||||
| 		impl->cache_params = true; | ||||
| 
 | ||||
| 	if ((str = pw_properties_get(node->properties, PW_KEY_NODE_DRIVER))) | ||||
| 		driver = pw_properties_parse_bool(str); | ||||
| 	else | ||||
|  | @ -1071,6 +1081,9 @@ struct pw_impl_node *pw_context_create_node(struct pw_context *context, | |||
| 		goto error_exit; | ||||
| 	} | ||||
| 
 | ||||
| 	spa_list_init(&impl->param_list); | ||||
| 	spa_list_init(&impl->pending_list); | ||||
| 
 | ||||
| 	this = &impl->this; | ||||
| 	this->context = context; | ||||
| 	this->name = strdup("node"); | ||||
|  | @ -1229,6 +1242,7 @@ int pw_impl_node_update_properties(struct pw_impl_node *node, const struct spa_d | |||
| static void node_info(void *data, const struct spa_node_info *info) | ||||
| { | ||||
| 	struct pw_impl_node *node = data; | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this); | ||||
| 	uint32_t changed_ids[MAX_PARAMS], n_changed_ids = 0; | ||||
| 	bool flags_changed = false; | ||||
| 
 | ||||
|  | @ -1256,16 +1270,22 @@ static void node_info(void *data, const struct spa_node_info *info) | |||
| 		node->info.n_params = SPA_MIN(info->n_params, SPA_N_ELEMENTS(node->params)); | ||||
| 
 | ||||
| 		for (i = 0; i < node->info.n_params; i++) { | ||||
| 			uint32_t id = info->params[i].id; | ||||
| 
 | ||||
| 			pw_log_debug(NAME" %p: param %d id:%d (%s) %08x:%08x", node, i, | ||||
| 					info->params[i].id, | ||||
| 					spa_debug_type_find_name(spa_type_param, info->params[i].id), | ||||
| 					id, spa_debug_type_find_name(spa_type_param, id), | ||||
| 					node->info.params[i].flags, info->params[i].flags); | ||||
| 
 | ||||
| 			if (node->info.params[i].flags != info->params[i].flags && | ||||
| 			    info->params[i].flags & SPA_PARAM_INFO_READ) | ||||
| 				changed_ids[n_changed_ids++] = info->params[i].id; | ||||
| 			if (node->info.params[i].flags == info->params[i].flags) | ||||
| 				continue; | ||||
| 
 | ||||
| 			pw_log_debug(NAME" %p: update param %d", node, id); | ||||
| 			pw_param_clear(&impl->pending_list, id); | ||||
| 			node->info.params[i] = info->params[i]; | ||||
| 			node->info.params[i].user = 0; | ||||
| 
 | ||||
| 			if (info->params[i].flags & SPA_PARAM_INFO_READ) | ||||
| 				changed_ids[n_changed_ids++] = id; | ||||
| 		} | ||||
| 	} | ||||
| 	emit_info_changed(node, flags_changed); | ||||
|  | @ -1686,6 +1706,9 @@ void pw_impl_node_destroy(struct pw_impl_node *node) | |||
| 
 | ||||
| 	pw_work_queue_destroy(impl->work); | ||||
| 
 | ||||
| 	pw_param_clear(&impl->param_list, SPA_ID_INVALID); | ||||
| 	pw_param_clear(&impl->pending_list, SPA_ID_INVALID); | ||||
| 
 | ||||
| 	pw_map_clear(&node->input_port_map); | ||||
| 	pw_map_clear(&node->output_port_map); | ||||
| 
 | ||||
|  | @ -1719,22 +1742,28 @@ int pw_impl_node_for_each_port(struct pw_impl_node *node, | |||
| } | ||||
| 
 | ||||
| struct result_node_params_data { | ||||
| 	struct impl *impl; | ||||
| 	void *data; | ||||
| 	int (*callback) (void *data, int seq, | ||||
| 			uint32_t id, uint32_t index, uint32_t next, | ||||
| 			struct spa_pod *param); | ||||
| 	int seq; | ||||
| 	unsigned int cache:1; | ||||
| }; | ||||
| 
 | ||||
| static void result_node_params(void *data, int seq, int res, uint32_t type, const void *result) | ||||
| { | ||||
| 	struct result_node_params_data *d = data; | ||||
| 	struct impl *impl = d->impl; | ||||
| 	switch (type) { | ||||
| 	case SPA_RESULT_TYPE_NODE_PARAMS: | ||||
| 	{ | ||||
| 		const struct spa_result_node_params *r = result; | ||||
| 		if (d->seq == seq) | ||||
| 		if (d->seq == seq) { | ||||
| 			d->callback(d->data, seq, r->id, r->index, r->next, r->param); | ||||
| 			if (d->cache) | ||||
| 				pw_param_add(&impl->pending_list, r->id, r->param); | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 	default: | ||||
|  | @ -1753,27 +1782,73 @@ int pw_impl_node_for_each_param(struct pw_impl_node *node, | |||
| 			   void *data) | ||||
| { | ||||
| 	int res; | ||||
| 	struct result_node_params_data user_data = { data, callback, seq }; | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this); | ||||
| 	struct result_node_params_data user_data = { impl, data, callback, seq, false }; | ||||
| 	struct spa_hook listener; | ||||
| 	struct spa_param_info *pi; | ||||
| 	static const struct spa_node_events node_events = { | ||||
| 		SPA_VERSION_NODE_EVENTS, | ||||
| 		.result = result_node_params, | ||||
| 	}; | ||||
| 
 | ||||
| 	pi = pw_param_info_find(node->info.params, node->info.n_params, param_id); | ||||
| 	if (pi == NULL) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	if (max == 0) | ||||
| 		max = UINT32_MAX; | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u", node, param_id, | ||||
| 	pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u cached:%d", node, param_id, | ||||
| 			spa_debug_type_find_name(spa_type_param, param_id), | ||||
| 			index, max); | ||||
| 			index, max, pi->user); | ||||
| 
 | ||||
| 	spa_zero(listener); | ||||
| 	spa_node_add_listener(node->node, &listener, &node_events, &user_data); | ||||
| 	res = spa_node_enum_params(node->node, seq, | ||||
| 					param_id, index, max, | ||||
| 					filter); | ||||
| 	spa_hook_remove(&listener); | ||||
| 	if (pi->user == 1) { | ||||
| 		struct pw_param *p; | ||||
| 		uint8_t buffer[1024]; | ||||
| 		struct spa_pod_builder b = { 0 }; | ||||
| 	        struct spa_result_node_params result; | ||||
| 		uint32_t count = 0; | ||||
| 		bool found = false; | ||||
| 
 | ||||
| 		result.id = param_id; | ||||
| 		result.next = 0; | ||||
| 
 | ||||
| 		spa_list_for_each(p, &impl->param_list, link) { | ||||
| 			result.index = result.next++; | ||||
| 			if (p->id != param_id) | ||||
| 				continue; | ||||
| 
 | ||||
| 			found = true; | ||||
| 
 | ||||
| 			if (result.index < index) | ||||
| 				continue; | ||||
| 
 | ||||
| 			spa_pod_builder_init(&b, buffer, sizeof(buffer)); | ||||
| 			if (spa_pod_filter(&b, &result.param, p->param, filter) != 0) | ||||
| 				continue; | ||||
| 
 | ||||
| 			pw_log_debug(NAME " %p: %d param %u", node, seq, result.index); | ||||
| 			result_node_params(&user_data, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); | ||||
| 
 | ||||
| 			if (++count == max) | ||||
| 				break; | ||||
| 		} | ||||
| 		res = found ? 0 : -ENOENT; | ||||
| 	} else { | ||||
| 		user_data.cache = impl->cache_params && filter == NULL; | ||||
| 
 | ||||
| 		spa_zero(listener); | ||||
| 		spa_node_add_listener(node->node, &listener, &node_events, &user_data); | ||||
| 		res = spa_node_enum_params(node->node, seq, | ||||
| 						param_id, index, max, | ||||
| 						filter); | ||||
| 		spa_hook_remove(&listener); | ||||
| 
 | ||||
| 		if (user_data.cache) { | ||||
| 			pw_param_update(&impl->param_list, &impl->pending_list); | ||||
| 			pi->user = 1; | ||||
| 		} | ||||
| 	} | ||||
| 	return res; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ | |||
| #include <spa/node/utils.h> | ||||
| #include <spa/utils/names.h> | ||||
| #include <spa/debug/types.h> | ||||
| #include <spa/pod/filter.h> | ||||
| 
 | ||||
| #include "pipewire/impl.h" | ||||
| #include "pipewire/private.h" | ||||
|  | @ -41,6 +42,11 @@ | |||
| struct impl { | ||||
| 	struct pw_impl_port this; | ||||
| 	struct spa_node mix_node;	/**< mix node implementation */ | ||||
| 
 | ||||
| 	struct spa_list param_list; | ||||
| 	struct spa_list pending_list; | ||||
| 
 | ||||
| 	unsigned int cache_params:1; | ||||
| }; | ||||
| 
 | ||||
| #define pw_port_resource(r,m,v,...)	pw_resource_call(r,struct pw_port_events,m,v,__VA_ARGS__) | ||||
|  | @ -338,8 +344,12 @@ static void emit_params(struct pw_impl_port *port, uint32_t *changed_ids, uint32 | |||
| 
 | ||||
| static void update_info(struct pw_impl_port *port, const struct spa_port_info *info) | ||||
| { | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this); | ||||
| 	uint32_t changed_ids[MAX_PARAMS], n_changed_ids = 0; | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: flags:%08"PRIx64" change_mask:%08"PRIx64, | ||||
| 			port, info->flags, info->change_mask); | ||||
| 
 | ||||
| 	if (info->change_mask & SPA_PORT_CHANGE_MASK_FLAGS) { | ||||
| 		port->spa_flags = info->flags; | ||||
| 	} | ||||
|  | @ -357,11 +367,22 @@ static void update_info(struct pw_impl_port *port, const struct spa_port_info *i | |||
| 		port->info.n_params = SPA_MIN(info->n_params, SPA_N_ELEMENTS(port->params)); | ||||
| 
 | ||||
| 		for (i = 0; i < port->info.n_params; i++) { | ||||
| 			if (port->info.params[i].flags != info->params[i].flags && | ||||
| 			    info->params[i].flags & SPA_PARAM_INFO_READ) | ||||
| 				changed_ids[n_changed_ids++] = info->params[i].id; | ||||
| 			uint32_t id = info->params[i].id; | ||||
| 
 | ||||
| 			pw_log_debug(NAME" %p: param %d id:%d (%s) %08x:%08x", port, i, | ||||
| 					id, spa_debug_type_find_name(spa_type_param, id), | ||||
| 					port->info.params[i].flags, info->params[i].flags); | ||||
| 
 | ||||
| 			if (port->info.params[i].flags == info->params[i].flags) | ||||
| 				continue; | ||||
| 
 | ||||
| 			pw_log_debug(NAME" %p: update param %d", port, id); | ||||
| 			pw_param_clear(&impl->pending_list, id); | ||||
| 			port->info.params[i] = info->params[i]; | ||||
| 			port->info.params[i].user = 0; | ||||
| 
 | ||||
| 			if (info->params[i].flags & SPA_PARAM_INFO_READ) | ||||
| 				changed_ids[n_changed_ids++] = id; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -387,6 +408,10 @@ struct pw_impl_port *pw_context_create_port( | |||
| 	if (impl == NULL) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	spa_list_init(&impl->param_list); | ||||
| 	spa_list_init(&impl->pending_list); | ||||
| 	impl->cache_params = true; | ||||
| 
 | ||||
| 	this = &impl->this; | ||||
| 	pw_log_debug(NAME" %p: new %s %d", this, | ||||
| 			pw_direction_as_string(direction), port_id); | ||||
|  | @ -994,6 +1019,7 @@ static void pw_impl_port_remove(struct pw_impl_port *port) | |||
| 
 | ||||
| void pw_impl_port_destroy(struct pw_impl_port *port) | ||||
| { | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this); | ||||
| 	struct pw_control *control; | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: destroy", port); | ||||
|  | @ -1024,6 +1050,9 @@ void pw_impl_port_destroy(struct pw_impl_port *port) | |||
| 	pw_buffers_clear(&port->mix_buffers); | ||||
| 	free((void*)port->error); | ||||
| 
 | ||||
| 	pw_param_clear(&impl->param_list, SPA_ID_INVALID); | ||||
| 	pw_param_clear(&impl->pending_list, SPA_ID_INVALID); | ||||
| 
 | ||||
| 	pw_map_clear(&port->mix_port_map); | ||||
| 
 | ||||
| 	pw_properties_free(port->properties); | ||||
|  | @ -1032,22 +1061,28 @@ void pw_impl_port_destroy(struct pw_impl_port *port) | |||
| } | ||||
| 
 | ||||
| struct result_port_params_data { | ||||
| 	struct impl *impl; | ||||
| 	void *data; | ||||
| 	int (*callback) (void *data, int seq, | ||||
| 			uint32_t id, uint32_t index, uint32_t next, | ||||
| 			struct spa_pod *param); | ||||
| 	int seq; | ||||
| 	unsigned int cache:1; | ||||
| }; | ||||
| 
 | ||||
| static void result_port_params(void *data, int seq, int res, uint32_t type, const void *result) | ||||
| { | ||||
| 	struct result_port_params_data *d = data; | ||||
| 	struct impl *impl = d->impl; | ||||
| 	switch (type) { | ||||
| 	case SPA_RESULT_TYPE_NODE_PARAMS: | ||||
| 	{ | ||||
| 		const struct spa_result_node_params *r = result; | ||||
| 		if (d->seq == seq) | ||||
| 		if (d->seq == seq) { | ||||
| 			d->callback(d->data, seq, r->id, r->index, r->next, r->param); | ||||
| 			if (d->cache) | ||||
| 				pw_param_add(&impl->pending_list, r->id, r->param); | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 	default: | ||||
|  | @ -1066,28 +1101,75 @@ int pw_impl_port_for_each_param(struct pw_impl_port *port, | |||
| 			   void *data) | ||||
| { | ||||
| 	int res; | ||||
| 	struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this); | ||||
| 	struct pw_impl_node *node = port->node; | ||||
| 	struct result_port_params_data user_data = { data, callback, seq }; | ||||
| 	struct result_port_params_data user_data = { impl, data, callback, seq, false }; | ||||
| 	struct spa_hook listener; | ||||
| 	struct spa_param_info *pi; | ||||
| 	static const struct spa_node_events node_events = { | ||||
| 		SPA_VERSION_NODE_EVENTS, | ||||
| 		.result = result_port_params, | ||||
| 	}; | ||||
| 
 | ||||
| 	pi = pw_param_info_find(port->info.params, port->info.n_params, param_id); | ||||
| 	if (pi == NULL) | ||||
| 		return -ENOENT; | ||||
| 
 | ||||
| 	if (max == 0) | ||||
| 		max = UINT32_MAX; | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u", port, param_id, | ||||
| 	pw_log_debug(NAME" %p: params id:%d (%s) index:%u max:%u cached:%d", port, param_id, | ||||
| 			spa_debug_type_find_name(spa_type_param, param_id), | ||||
| 			index, max); | ||||
| 			index, max, pi->user); | ||||
| 
 | ||||
| 	spa_zero(listener); | ||||
| 	spa_node_add_listener(node->node, &listener, &node_events, &user_data); | ||||
| 	res = spa_node_port_enum_params(node->node, seq, | ||||
| 					port->direction, port->port_id, | ||||
| 					param_id, index, max, | ||||
| 					filter); | ||||
| 	spa_hook_remove(&listener); | ||||
| 	if (pi->user == 1) { | ||||
| 		struct pw_param *p; | ||||
| 		uint8_t buffer[1024]; | ||||
| 		struct spa_pod_builder b = { 0 }; | ||||
| 	        struct spa_result_node_params result; | ||||
| 		uint32_t count = 0; | ||||
| 		bool found = false; | ||||
| 
 | ||||
| 		result.id = param_id; | ||||
| 		result.next = 0; | ||||
| 
 | ||||
| 		spa_list_for_each(p, &impl->param_list, link) { | ||||
| 			result.index = result.next++; | ||||
| 			if (p->id != param_id) | ||||
| 				continue; | ||||
| 
 | ||||
| 			found = true; | ||||
| 
 | ||||
| 			if (result.index < index) | ||||
| 				continue; | ||||
| 
 | ||||
| 			spa_pod_builder_init(&b, buffer, sizeof(buffer)); | ||||
| 			if (spa_pod_filter(&b, &result.param, p->param, filter) != 0) | ||||
| 				continue; | ||||
| 
 | ||||
| 			pw_log_debug(NAME " %p: %d param %u", port, seq, result.index); | ||||
| 			result_port_params(&user_data, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); | ||||
| 
 | ||||
| 			if (++count == max) | ||||
| 				break; | ||||
| 		} | ||||
| 		res = found ? 0 : -ENOENT; | ||||
| 	} else { | ||||
| 		user_data.cache = filter == NULL; | ||||
| 
 | ||||
| 		spa_zero(listener); | ||||
| 		spa_node_add_listener(node->node, &listener, &node_events, &user_data); | ||||
| 		res = spa_node_port_enum_params(node->node, seq, | ||||
| 						port->direction, port->port_id, | ||||
| 						param_id, index, max, | ||||
| 						filter); | ||||
| 		spa_hook_remove(&listener); | ||||
| 
 | ||||
| 		if (user_data.cache) { | ||||
| 			pw_param_update(&impl->param_list, &impl->pending_list); | ||||
| 			pi->user = 1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pw_log_debug(NAME" %p: res %d: (%s)", port, res, spa_strerror(res)); | ||||
| 	return res; | ||||
|  |  | |||
|  | @ -142,6 +142,7 @@ extern "C" { | |||
| #define PW_KEY_NODE_DONT_RECONNECT	"node.dont-reconnect"	/**< don't reconnect this node */ | ||||
| #define PW_KEY_NODE_ALWAYS_PROCESS	"node.always-process"	/**< process even when unlinked */ | ||||
| #define PW_KEY_NODE_PAUSE_ON_IDLE	"node.pause-on-idle"	/**< pause the node when idle */ | ||||
| #define PW_KEY_NODE_CACHE_PARAMS	"node.cache-params"	/**< cache the node params */ | ||||
| #define PW_KEY_NODE_DRIVER		"node.driver"		/**< node can drive the graph */ | ||||
| #define PW_KEY_NODE_STREAM		"node.stream"		/**< node is a stream, the server side should | ||||
| 								  *  add a converter */ | ||||
|  | @ -155,6 +156,7 @@ extern "C" { | |||
| #define PW_KEY_PORT_TERMINAL		"port.terminal"		/**< if this port consumes the data */ | ||||
| #define PW_KEY_PORT_CONTROL		"port.control"		/**< if this port is a control port */ | ||||
| #define PW_KEY_PORT_MONITOR		"port.monitor"		/**< if this port is a monitor port */ | ||||
| #define PW_KEY_PORT_CACHE_PARAMS	"port.cache-params"	/**< cache the node port params */ | ||||
| 
 | ||||
| /** link properties */ | ||||
| #define PW_KEY_LINK_ID			"link.id"		/**< a link id */ | ||||
|  | @ -202,6 +204,7 @@ extern "C" { | |||
| 								  *  roles (see PW_KEY_MEDIA_ROLE) this device | ||||
| 								  *  is particularly well suited for, due to | ||||
| 								  *  latency, quality or form factor. */ | ||||
| #define PW_KEY_DEVICE_CACHE_PARAMS	"device.cache-params"	/**< cache the device spa params */ | ||||
| 
 | ||||
| /** module properties */ | ||||
| #define PW_KEY_MODULE_ID		"module.id"		/**< the module id */ | ||||
|  |  | |||
|  | @ -84,6 +84,73 @@ static inline bool ratelimit_test(struct ratelimit *r, uint64_t now) | |||
| 
 | ||||
| #define MAX_PARAMS	32 | ||||
| 
 | ||||
| struct pw_param { | ||||
| 	uint32_t id; | ||||
| 	struct spa_list link; | ||||
| 	struct spa_pod *param; | ||||
| }; | ||||
| 
 | ||||
| static inline struct pw_param *pw_param_add(struct spa_list *params, | ||||
| 		uint32_t id, const struct spa_pod *param) | ||||
| { | ||||
| 	struct pw_param *p; | ||||
| 
 | ||||
| 	if (param == NULL || !spa_pod_is_object(param)) { | ||||
| 		errno = EINVAL; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (id == SPA_ID_INVALID) | ||||
| 		id = SPA_POD_OBJECT_ID(param); | ||||
| 
 | ||||
| 	if ((p = malloc(sizeof(*p) + SPA_POD_SIZE(param))) == NULL) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	p->id = id; | ||||
| 	p->param = SPA_MEMBER(p, sizeof(*p), struct spa_pod); | ||||
| 	memcpy(p->param, param, SPA_POD_SIZE(param)); | ||||
| 	spa_list_append(params, &p->link); | ||||
| 	return p; | ||||
| } | ||||
| 
 | ||||
| static inline uint32_t pw_param_clear(struct spa_list *param_list, uint32_t id) | ||||
| { | ||||
| 	struct pw_param *p, *t; | ||||
| 	uint32_t count = 0; | ||||
| 
 | ||||
| 	spa_list_for_each_safe(p, t, param_list, link) { | ||||
| 		if (id == SPA_ID_INVALID || p->id == id) { | ||||
| 			spa_list_remove(&p->link); | ||||
| 			free(p); | ||||
| 			count++; | ||||
| 		} | ||||
| 	} | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static inline void pw_param_update(struct spa_list *param_list, struct spa_list *pending_list) | ||||
| { | ||||
| 	struct pw_param *p; | ||||
| 
 | ||||
| 	spa_list_for_each(p, pending_list, link) | ||||
| 		pw_param_clear(param_list, p->id); | ||||
| 
 | ||||
| 	spa_list_consume(p, pending_list, link) { | ||||
| 		spa_list_remove(&p->link); | ||||
| 		spa_list_append(param_list, &p->link); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static inline struct spa_param_info *pw_param_info_find(struct spa_param_info info[], | ||||
| 		uint32_t n_info, uint32_t id) | ||||
| { | ||||
| 	uint32_t i; | ||||
| 	for (i = 0; i < n_info; i++) { | ||||
| 		if (info[i].id == id) | ||||
| 			return &info[i]; | ||||
| 	} | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| #define pw_protocol_emit_destroy(p) spa_hook_list_call(&p->listener_list, struct pw_protocol_events, destroy, 0) | ||||
| 
 | ||||
| struct pw_protocol { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Wim Taymans
						Wim Taymans