mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	pulse-server: emulate synchronous MOVE_* commands
Make MOVE_SINK_INPUT/MOVE_SOURCE_OUTPUT change the linked peer immediately in subsequent GET_SINK_INPUT_INFO/GET_SOURCE_OUTPUT_INFO commands. Do this by keeping track of the sink/source where the client moved the stream to, and temporarily replying so in future GET_INFO (but only in messages for that client). We discard the temporary override when we either get an update event for the stream (i.e. SM moved the stream), or a 1sec timer runs out. If the timer runs out, we emit a sink-input/source-output change event, as in that case what we claimed in the earlier GET_INFO messages might not be true, so clients need to update their information. This gets rid of race conditions where an application moves a stream, and expects the move to be visible in future GET_INFO replies, which may fail to happen because it takes some time for the session manager to re-link the streams. Fixes pasystray behavior.
This commit is contained in:
		
							parent
							
								
									83be5d866f
								
							
						
					
					
						commit
						98aa7ccff0
					
				
					 1 changed files with 111 additions and 14 deletions
				
			
		| 
						 | 
				
			
			@ -88,11 +88,18 @@
 | 
			
		|||
 | 
			
		||||
#define MAX_FORMATS	32
 | 
			
		||||
 | 
			
		||||
#define TEMPORARY_MOVE_TIMEOUT	(SPA_NSEC_PER_SEC)
 | 
			
		||||
 | 
			
		||||
bool debug_messages = false;
 | 
			
		||||
 | 
			
		||||
struct latency_offset_data {
 | 
			
		||||
	int64_t prev_latency_offset;
 | 
			
		||||
	unsigned int initialized:1;
 | 
			
		||||
	uint8_t initialized:1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct temporary_move_data {
 | 
			
		||||
	uint32_t peer_index;
 | 
			
		||||
	uint8_t used:1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct sample *find_sample(struct impl *impl, uint32_t index, const char *name)
 | 
			
		||||
| 
						 | 
				
			
			@ -256,6 +263,75 @@ static int send_object_event(struct client *client, struct pw_manager_object *o,
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint32_t get_temporary_move_target(struct client *client, struct pw_manager_object *o)
 | 
			
		||||
{
 | 
			
		||||
	struct temporary_move_data *d;
 | 
			
		||||
 | 
			
		||||
	d = pw_manager_object_get_data(o, "temporary_move_data");
 | 
			
		||||
	if (d == NULL || d->peer_index == SPA_ID_INVALID)
 | 
			
		||||
		return SPA_ID_INVALID;
 | 
			
		||||
 | 
			
		||||
	pw_log_debug("[%s] using temporary move target for index:%d -> index:%d",
 | 
			
		||||
			client->name, o->index, d->peer_index);
 | 
			
		||||
	d->used = true;
 | 
			
		||||
	return d->peer_index;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_temporary_move_target(struct client *client, struct pw_manager_object *o, uint32_t index)
 | 
			
		||||
{
 | 
			
		||||
	struct temporary_move_data *d;
 | 
			
		||||
 | 
			
		||||
	if (!pw_manager_object_is_sink_input(o) && !pw_manager_object_is_source_output(o))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (index == SPA_ID_INVALID) {
 | 
			
		||||
		d = pw_manager_object_get_data(o, "temporary_move_data");
 | 
			
		||||
		if (d == NULL)
 | 
			
		||||
			return;
 | 
			
		||||
		if (d->peer_index != SPA_ID_INVALID)
 | 
			
		||||
			pw_log_debug("cleared temporary move target for index:%d", o->index);
 | 
			
		||||
		d->peer_index = SPA_ID_INVALID;
 | 
			
		||||
		d->used = false;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d = pw_manager_object_add_temporary_data(o, "temporary_move_data",
 | 
			
		||||
			sizeof(struct temporary_move_data),
 | 
			
		||||
			TEMPORARY_MOVE_TIMEOUT);
 | 
			
		||||
	if (d == NULL)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	pw_log_debug("[%s] set temporary move target for index:%d to index:%d",
 | 
			
		||||
			client->name, o->index, index);
 | 
			
		||||
	d->peer_index = index;
 | 
			
		||||
	d->used = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void temporary_move_target_timeout(struct client *client, struct pw_manager_object *o)
 | 
			
		||||
{
 | 
			
		||||
	struct temporary_move_data *d = pw_manager_object_get_data(o, "temporary_move_data");
 | 
			
		||||
	struct pw_manager_object *peer;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Send change event if the temporary data was used, and the peer
 | 
			
		||||
	 * is not what we claimed.
 | 
			
		||||
	 */
 | 
			
		||||
 | 
			
		||||
	if (d == NULL || d->peer_index == SPA_ID_INVALID || !d->used)
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	peer = find_linked(client->manager, o->id, pw_manager_object_is_sink_input(o) ?
 | 
			
		||||
			PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT);
 | 
			
		||||
	if (peer == NULL || peer->index != d->peer_index) {
 | 
			
		||||
		pw_log_debug("[%s] temporary move timeout for index:%d, send change event",
 | 
			
		||||
				client->name, o->index);
 | 
			
		||||
		send_object_event(client, o, SUBSCRIPTION_EVENT_CHANGE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	set_temporary_move_target(client, o, SPA_ID_INVALID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct pw_manager_object *find_device(struct client *client,
 | 
			
		||||
		uint32_t index, const char *name, bool sink, bool *is_monitor);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -773,6 +849,8 @@ static void manager_updated(void *data, struct pw_manager_object *o)
 | 
			
		|||
 | 
			
		||||
	send_object_event(client, o, SUBSCRIPTION_EVENT_CHANGE);
 | 
			
		||||
 | 
			
		||||
	set_temporary_move_target(client, o, SPA_ID_INVALID);
 | 
			
		||||
 | 
			
		||||
	send_latency_offset_subscribe_event(client, o);
 | 
			
		||||
	send_default_change_subscribe_event(client, pw_manager_object_is_sink(o), pw_manager_object_is_source_or_monitor(o));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -793,6 +871,14 @@ static void manager_removed(void *data, struct pw_manager_object *o)
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void manager_object_data_timeout(void *data, struct pw_manager_object *o, const char *key)
 | 
			
		||||
{
 | 
			
		||||
	struct client *client = data;
 | 
			
		||||
 | 
			
		||||
	if (spa_streq(key, "temporary_move_data"))
 | 
			
		||||
		temporary_move_target_timeout(client, o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int json_object_find(const char *obj, const char *key, char *value, size_t len)
 | 
			
		||||
{
 | 
			
		||||
	struct spa_json it[2];
 | 
			
		||||
| 
						 | 
				
			
			@ -887,7 +973,8 @@ static const struct pw_manager_events manager_events = {
 | 
			
		|||
	.updated = manager_updated,
 | 
			
		||||
	.removed = manager_removed,
 | 
			
		||||
	.metadata = manager_metadata,
 | 
			
		||||
	.disconnect = manager_disconnect
 | 
			
		||||
	.disconnect = manager_disconnect,
 | 
			
		||||
	.object_data_timeout = manager_object_data_timeout,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int do_set_client_name(struct client *client, uint32_t command, uint32_t tag, struct message *m)
 | 
			
		||||
| 
						 | 
				
			
			@ -3940,7 +4027,6 @@ static int fill_sink_input_info(struct client *client, struct message *m,
 | 
			
		|||
	struct impl *impl = client->impl;
 | 
			
		||||
	struct pw_node_info *info = o->info;
 | 
			
		||||
	struct pw_manager *manager = client->manager;
 | 
			
		||||
	struct pw_manager_object *peer;
 | 
			
		||||
	const char *str;
 | 
			
		||||
	uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
 | 
			
		||||
	uint32_t peer_index;
 | 
			
		||||
| 
						 | 
				
			
			@ -3966,11 +4052,15 @@ static int fill_sink_input_info(struct client *client, struct message *m,
 | 
			
		|||
	    !volume_valid(&dev_info.volume_info.volume))
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	peer = find_linked(manager, o->id, PW_DIRECTION_OUTPUT);
 | 
			
		||||
	if (peer && pw_manager_object_is_sink(peer))
 | 
			
		||||
		peer_index = peer->index;
 | 
			
		||||
	else
 | 
			
		||||
		peer_index = SPA_ID_INVALID;
 | 
			
		||||
	peer_index = get_temporary_move_target(client, o);
 | 
			
		||||
	if (peer_index == SPA_ID_INVALID) {
 | 
			
		||||
		struct pw_manager_object *peer;
 | 
			
		||||
		peer = find_linked(manager, o->id, PW_DIRECTION_OUTPUT);
 | 
			
		||||
		if (peer && pw_manager_object_is_sink(peer))
 | 
			
		||||
			peer_index = peer->index;
 | 
			
		||||
		else
 | 
			
		||||
			peer_index = SPA_ID_INVALID;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	message_put(m,
 | 
			
		||||
		TAG_U32, o->index,				/* sink_input index */
 | 
			
		||||
| 
						 | 
				
			
			@ -4020,7 +4110,6 @@ static int fill_source_output_info(struct client *client, struct message *m,
 | 
			
		|||
	struct impl *impl = client->impl;
 | 
			
		||||
	struct pw_node_info *info = o->info;
 | 
			
		||||
	struct pw_manager *manager = client->manager;
 | 
			
		||||
	struct pw_manager_object *peer;
 | 
			
		||||
	const char *str;
 | 
			
		||||
	uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
 | 
			
		||||
	uint32_t peer_index;
 | 
			
		||||
| 
						 | 
				
			
			@ -4046,11 +4135,15 @@ static int fill_source_output_info(struct client *client, struct message *m,
 | 
			
		|||
	    !volume_valid(&dev_info.volume_info.volume))
 | 
			
		||||
		return -ENOENT;
 | 
			
		||||
 | 
			
		||||
	peer = find_linked(manager, o->id, PW_DIRECTION_INPUT);
 | 
			
		||||
	if (peer && pw_manager_object_is_source_or_monitor(peer))
 | 
			
		||||
		peer_index = peer->index;
 | 
			
		||||
	else
 | 
			
		||||
		peer_index = SPA_ID_INVALID;
 | 
			
		||||
	peer_index = get_temporary_move_target(client, o);
 | 
			
		||||
	if (peer_index == SPA_ID_INVALID) {
 | 
			
		||||
		struct pw_manager_object *peer;
 | 
			
		||||
		peer = find_linked(manager, o->id, PW_DIRECTION_INPUT);
 | 
			
		||||
		if (peer && pw_manager_object_is_source_or_monitor(peer))
 | 
			
		||||
			peer_index = peer->index;
 | 
			
		||||
		else
 | 
			
		||||
			peer_index = SPA_ID_INVALID;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	message_put(m,
 | 
			
		||||
		TAG_U32, o->index,				/* source_output index */
 | 
			
		||||
| 
						 | 
				
			
			@ -4695,6 +4788,10 @@ static int do_move_stream(struct client *client, uint32_t command, uint32_t tag,
 | 
			
		|||
			SPA_TYPE_INFO_BASE"Id", "%"PRIi64, target_serial)) < 0)
 | 
			
		||||
		return res;
 | 
			
		||||
 | 
			
		||||
	/* We will temporarily claim the stream was already moved */
 | 
			
		||||
	set_temporary_move_target(client, o, dev->index);
 | 
			
		||||
	send_object_event(client, o, SUBSCRIPTION_EVENT_CHANGE);
 | 
			
		||||
 | 
			
		||||
	return reply_simple_ack(client, tag);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue