mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	module-pulse-tunnel: don't block the main thread
Do the pulse context and stream connect async so that we don't have to block the main thread. Fixes #3221
This commit is contained in:
		
							parent
							
								
									6ae9698ebc
								
							
						
					
					
						commit
						6c772a1843
					
				
					 1 changed files with 150 additions and 153 deletions
				
			
		| 
						 | 
					@ -518,42 +518,37 @@ static void module_schedule_destroy(struct impl *impl)
 | 
				
			||||||
	pw_loop_invoke(impl->main_loop, do_schedule_destroy, 1, NULL, 0, false, impl);
 | 
						pw_loop_invoke(impl->main_loop, do_schedule_destroy, 1, NULL, 0, false, impl);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void context_state_cb(pa_context *c, void *userdata)
 | 
					static int
 | 
				
			||||||
 | 
					do_create_stream(struct spa_loop *loop,
 | 
				
			||||||
 | 
						bool async, uint32_t seq, const void *data, size_t size, void *user_data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct impl *impl = userdata;
 | 
						struct impl *impl = user_data;
 | 
				
			||||||
	bool do_destroy = false;
 | 
						int res;
 | 
				
			||||||
	switch (pa_context_get_state(c)) {
 | 
						if (impl->stream == NULL) {
 | 
				
			||||||
	case PA_CONTEXT_TERMINATED:
 | 
							if ((res = create_stream(impl)) < 0) {
 | 
				
			||||||
	case PA_CONTEXT_FAILED:
 | 
								pw_log_error("failed to create stream: %s", spa_strerror(res));
 | 
				
			||||||
		do_destroy = true;
 | 
								do_schedule_destroy(loop, async, seq, NULL, 0, user_data);
 | 
				
			||||||
		SPA_FALLTHROUGH;
 | 
					 | 
				
			||||||
	case PA_CONTEXT_READY:
 | 
					 | 
				
			||||||
		pa_threaded_mainloop_signal(impl->pa_mainloop, 0);
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case PA_CONTEXT_UNCONNECTED:
 | 
					 | 
				
			||||||
		do_destroy = true;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	case PA_CONTEXT_CONNECTING:
 | 
					 | 
				
			||||||
	case PA_CONTEXT_AUTHORIZING:
 | 
					 | 
				
			||||||
	case PA_CONTEXT_SETTING_NAME:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	if (do_destroy)
 | 
						}
 | 
				
			||||||
		module_schedule_destroy(impl);
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void stream_state_cb(pa_stream *s, void * userdata)
 | 
					static void stream_state_cb(pa_stream *s, void * userdata)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct impl *impl = userdata;
 | 
						struct impl *impl = userdata;
 | 
				
			||||||
	bool do_destroy = false;
 | 
						bool do_destroy = false;
 | 
				
			||||||
	switch (pa_stream_get_state(s)) {
 | 
						pa_stream_state_t state = pa_stream_get_state(s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_debug("stream state %d", state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (state) {
 | 
				
			||||||
	case PA_STREAM_FAILED:
 | 
						case PA_STREAM_FAILED:
 | 
				
			||||||
	case PA_STREAM_TERMINATED:
 | 
						case PA_STREAM_TERMINATED:
 | 
				
			||||||
		do_destroy = true;
 | 
							do_destroy = true;
 | 
				
			||||||
		SPA_FALLTHROUGH;
 | 
							SPA_FALLTHROUGH;
 | 
				
			||||||
	case PA_STREAM_READY:
 | 
						case PA_STREAM_READY:
 | 
				
			||||||
		impl->pa_index = pa_stream_get_index(impl->pa_stream);
 | 
							impl->pa_index = pa_stream_get_index(impl->pa_stream);
 | 
				
			||||||
		pa_threaded_mainloop_signal(impl->pa_mainloop, 0);
 | 
							pw_loop_invoke(impl->main_loop, do_create_stream, 1, NULL, 0, false, impl);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case PA_STREAM_UNCONNECTED:
 | 
						case PA_STREAM_UNCONNECTED:
 | 
				
			||||||
		do_destroy = true;
 | 
							do_destroy = true;
 | 
				
			||||||
| 
						 | 
					@ -697,14 +692,96 @@ static void stream_overflow_cb(pa_stream *s, void *userdata)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void stream_latency_update_cb(pa_stream *s, void *userdata)
 | 
					static void stream_latency_update_cb(pa_stream *s, void *userdata)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct impl *impl = userdata;
 | 
					 | 
				
			||||||
	pa_usec_t usec;
 | 
						pa_usec_t usec;
 | 
				
			||||||
	int negative;
 | 
						int negative;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	pa_stream_get_latency(s, &usec, &negative);
 | 
						pa_stream_get_latency(s, &usec, &negative);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	pw_log_debug("latency %ld negative %d", usec, negative);
 | 
						pw_log_debug("latency %ld negative %d", usec, negative);
 | 
				
			||||||
	pa_threaded_mainloop_signal(impl->pa_mainloop, 0);
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int create_pulse_stream(struct impl *impl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pa_sample_spec ss;
 | 
				
			||||||
 | 
						pa_channel_map map;
 | 
				
			||||||
 | 
						uint32_t latency_bytes, i, aux = 0;
 | 
				
			||||||
 | 
						const char *remote_node_target;
 | 
				
			||||||
 | 
						char stream_name[1024];
 | 
				
			||||||
 | 
						pa_buffer_attr bufferattr;
 | 
				
			||||||
 | 
						int err = PA_ERR_IO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ss.format = (pa_sample_format_t) format_id2pa(impl->info.format);
 | 
				
			||||||
 | 
						ss.channels = impl->info.channels;
 | 
				
			||||||
 | 
						ss.rate = impl->info.rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						map.channels = impl->info.channels;
 | 
				
			||||||
 | 
						for (i = 0; i < map.channels; i++)
 | 
				
			||||||
 | 
							map.map[i] = (pa_channel_position_t)channel_id2pa(impl->info.position[i], &aux);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"),
 | 
				
			||||||
 | 
								pw_get_user_name(), pw_get_host_name());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_info("create stream %s", stream_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!(impl->pa_stream = pa_stream_new(impl->pa_context, stream_name, &ss, &map))) {
 | 
				
			||||||
 | 
							err = pa_context_errno(impl->pa_context);
 | 
				
			||||||
 | 
							goto exit;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pa_stream_set_state_callback(impl->pa_stream, stream_state_cb, impl);
 | 
				
			||||||
 | 
						pa_stream_set_read_callback(impl->pa_stream, stream_read_request_cb, impl);
 | 
				
			||||||
 | 
						pa_stream_set_write_callback(impl->pa_stream, stream_write_request_cb, impl);
 | 
				
			||||||
 | 
						pa_stream_set_underflow_callback(impl->pa_stream, stream_underflow_cb, impl);
 | 
				
			||||||
 | 
						pa_stream_set_overflow_callback(impl->pa_stream, stream_overflow_cb, impl);
 | 
				
			||||||
 | 
						pa_stream_set_latency_update_callback(impl->pa_stream, stream_latency_update_cb, impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						remote_node_target = pw_properties_get(impl->props, PW_KEY_TARGET_OBJECT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bufferattr.fragsize = (uint32_t) -1;
 | 
				
			||||||
 | 
						bufferattr.minreq = (uint32_t) -1;
 | 
				
			||||||
 | 
						bufferattr.maxlength = (uint32_t) -1;
 | 
				
			||||||
 | 
						bufferattr.prebuf = (uint32_t) -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						latency_bytes = pa_usec_to_bytes(impl->latency_msec * SPA_USEC_PER_MSEC, &ss);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->target_latency = latency_bytes / impl->frame_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* half in our buffer, half in the network + remote */
 | 
				
			||||||
 | 
						impl->target_buffer = latency_bytes / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (impl->mode == MODE_SOURCE) {
 | 
				
			||||||
 | 
							bufferattr.fragsize = latency_bytes / 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pa_context_subscribe(impl->pa_context,
 | 
				
			||||||
 | 
									PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((err = pa_stream_connect_record(impl->pa_stream,
 | 
				
			||||||
 | 
									remote_node_target, &bufferattr,
 | 
				
			||||||
 | 
									PA_STREAM_DONT_MOVE |
 | 
				
			||||||
 | 
									PA_STREAM_INTERPOLATE_TIMING |
 | 
				
			||||||
 | 
									PA_STREAM_ADJUST_LATENCY |
 | 
				
			||||||
 | 
									PA_STREAM_AUTO_TIMING_UPDATE)) != 0)
 | 
				
			||||||
 | 
								err = pa_context_errno(impl->pa_context);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							bufferattr.tlength = latency_bytes / 2;
 | 
				
			||||||
 | 
							bufferattr.minreq = bufferattr.tlength / 4;
 | 
				
			||||||
 | 
							bufferattr.prebuf = bufferattr.tlength;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pa_context_subscribe(impl->pa_context,
 | 
				
			||||||
 | 
									PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((err = pa_stream_connect_playback(impl->pa_stream,
 | 
				
			||||||
 | 
									remote_node_target, &bufferattr,
 | 
				
			||||||
 | 
									PA_STREAM_DONT_MOVE |
 | 
				
			||||||
 | 
									PA_STREAM_INTERPOLATE_TIMING |
 | 
				
			||||||
 | 
									PA_STREAM_ADJUST_LATENCY |
 | 
				
			||||||
 | 
									PA_STREAM_AUTO_TIMING_UPDATE,
 | 
				
			||||||
 | 
									NULL, NULL)) != 0)
 | 
				
			||||||
 | 
								err = pa_context_errno(impl->pa_context);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
						if (err != PA_OK)
 | 
				
			||||||
 | 
							pw_log_error("failed to create stream: %s", pa_strerror(err));
 | 
				
			||||||
 | 
						return err_to_res(err);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
| 
						 | 
					@ -779,6 +856,36 @@ static void context_subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
 | 
				
			||||||
				idx, sink_input_info_cb, impl);
 | 
									idx, sink_input_info_cb, impl);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void context_state_cb(pa_context *c, void *userdata)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = userdata;
 | 
				
			||||||
 | 
						bool do_destroy = false;
 | 
				
			||||||
 | 
						pa_context_state_t state = pa_context_get_state(c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_debug("state %d", state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (state) {
 | 
				
			||||||
 | 
						case PA_CONTEXT_TERMINATED:
 | 
				
			||||||
 | 
						case PA_CONTEXT_FAILED:
 | 
				
			||||||
 | 
							do_destroy = true;
 | 
				
			||||||
 | 
							SPA_FALLTHROUGH;
 | 
				
			||||||
 | 
						case PA_CONTEXT_READY:
 | 
				
			||||||
 | 
							if (impl->pa_stream == NULL)
 | 
				
			||||||
 | 
								if (create_pulse_stream(impl) < 0)
 | 
				
			||||||
 | 
									do_destroy = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case PA_CONTEXT_UNCONNECTED:
 | 
				
			||||||
 | 
							do_destroy = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case PA_CONTEXT_CONNECTING:
 | 
				
			||||||
 | 
						case PA_CONTEXT_AUTHORIZING:
 | 
				
			||||||
 | 
						case PA_CONTEXT_SETTING_NAME:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (do_destroy)
 | 
				
			||||||
 | 
							module_schedule_destroy(impl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static pa_proplist* tunnel_new_proplist(struct impl *impl)
 | 
					static pa_proplist* tunnel_new_proplist(struct impl *impl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pa_proplist *proplist = pa_proplist_new();
 | 
						pa_proplist *proplist = pa_proplist_new();
 | 
				
			||||||
| 
						 | 
					@ -788,20 +895,15 @@ static pa_proplist* tunnel_new_proplist(struct impl *impl)
 | 
				
			||||||
	return proplist;
 | 
						return proplist;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int create_pulse_stream(struct impl *impl)
 | 
					static int start_pulse_connection(struct impl *impl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pa_sample_spec ss;
 | 
						const char *server_address;
 | 
				
			||||||
	pa_channel_map map;
 | 
					 | 
				
			||||||
	const char *server_address, *remote_node_target;
 | 
					 | 
				
			||||||
	pa_proplist *props = NULL;
 | 
						pa_proplist *props = NULL;
 | 
				
			||||||
	pa_mainloop_api *api;
 | 
						pa_mainloop_api *api;
 | 
				
			||||||
	char stream_name[1024];
 | 
						int err = PA_ERR_IO;
 | 
				
			||||||
	pa_buffer_attr bufferattr;
 | 
					 | 
				
			||||||
	int res = -EIO;
 | 
					 | 
				
			||||||
	uint32_t latency_bytes, i, aux = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((impl->pa_mainloop = pa_threaded_mainloop_new()) == NULL)
 | 
						if ((impl->pa_mainloop = pa_threaded_mainloop_new()) == NULL)
 | 
				
			||||||
		goto error;
 | 
							goto exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	api = pa_threaded_mainloop_get_api(impl->pa_mainloop);
 | 
						api = pa_threaded_mainloop_get_api(impl->pa_mainloop);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -810,15 +912,17 @@ static int create_pulse_stream(struct impl *impl)
 | 
				
			||||||
	pa_proplist_free(props);
 | 
						pa_proplist_free(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (impl->pa_context == NULL)
 | 
						if (impl->pa_context == NULL)
 | 
				
			||||||
		goto error;
 | 
							goto exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pa_context_set_state_callback(impl->pa_context, context_state_cb, impl);
 | 
						pa_context_set_state_callback(impl->pa_context, context_state_cb, impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server_address = pw_properties_get(impl->props, "pulse.server.address");
 | 
						server_address = pw_properties_get(impl->props, "pulse.server.address");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pw_log_info("connecting to %s...", server_address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pa_context_connect(impl->pa_context, server_address, 0, NULL) < 0) {
 | 
						if (pa_context_connect(impl->pa_context, server_address, 0, NULL) < 0) {
 | 
				
			||||||
		res = pa_context_errno(impl->pa_context);
 | 
							err = pa_context_errno(impl->pa_context);
 | 
				
			||||||
		goto error;
 | 
							goto exit;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pa_threaded_mainloop_lock(impl->pa_mainloop);
 | 
						pa_threaded_mainloop_lock(impl->pa_mainloop);
 | 
				
			||||||
| 
						 | 
					@ -826,122 +930,18 @@ static int create_pulse_stream(struct impl *impl)
 | 
				
			||||||
	pa_context_set_subscribe_callback(impl->pa_context, context_subscribe_cb, impl);
 | 
						pa_context_set_subscribe_callback(impl->pa_context, context_subscribe_cb, impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pa_threaded_mainloop_start(impl->pa_mainloop) < 0)
 | 
						if (pa_threaded_mainloop_start(impl->pa_mainloop) < 0)
 | 
				
			||||||
		goto error_unlock;
 | 
							goto exit_unlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (;;) {
 | 
						err = PA_OK;
 | 
				
			||||||
		pa_context_state_t state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		state = pa_context_get_state(impl->pa_context);
 | 
					 | 
				
			||||||
		if (state == PA_CONTEXT_READY)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!PA_CONTEXT_IS_GOOD(state)) {
 | 
					 | 
				
			||||||
			res = pa_context_errno(impl->pa_context);
 | 
					 | 
				
			||||||
			goto error_unlock;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		/* Wait until the context is ready */
 | 
					 | 
				
			||||||
		pa_threaded_mainloop_wait(impl->pa_mainloop);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ss.format = (pa_sample_format_t) format_id2pa(impl->info.format);
 | 
					 | 
				
			||||||
	ss.channels = impl->info.channels;
 | 
					 | 
				
			||||||
	ss.rate = impl->info.rate;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	map.channels = impl->info.channels;
 | 
					 | 
				
			||||||
	for (i = 0; i < map.channels; i++)
 | 
					 | 
				
			||||||
		map.map[i] = (pa_channel_position_t)channel_id2pa(impl->info.position[i], &aux);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	snprintf(stream_name, sizeof(stream_name), _("Tunnel for %s@%s"),
 | 
					 | 
				
			||||||
			pw_get_user_name(), pw_get_host_name());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!(impl->pa_stream = pa_stream_new(impl->pa_context, stream_name, &ss, &map))) {
 | 
					 | 
				
			||||||
		res = pa_context_errno(impl->pa_context);
 | 
					 | 
				
			||||||
		goto error_unlock;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pa_stream_set_state_callback(impl->pa_stream, stream_state_cb, impl);
 | 
					 | 
				
			||||||
	pa_stream_set_read_callback(impl->pa_stream, stream_read_request_cb, impl);
 | 
					 | 
				
			||||||
	pa_stream_set_write_callback(impl->pa_stream, stream_write_request_cb, impl);
 | 
					 | 
				
			||||||
	pa_stream_set_underflow_callback(impl->pa_stream, stream_underflow_cb, impl);
 | 
					 | 
				
			||||||
	pa_stream_set_overflow_callback(impl->pa_stream, stream_overflow_cb, impl);
 | 
					 | 
				
			||||||
	pa_stream_set_latency_update_callback(impl->pa_stream, stream_latency_update_cb, impl);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	remote_node_target = pw_properties_get(impl->props, PW_KEY_TARGET_OBJECT);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bufferattr.fragsize = (uint32_t) -1;
 | 
					 | 
				
			||||||
	bufferattr.minreq = (uint32_t) -1;
 | 
					 | 
				
			||||||
	bufferattr.maxlength = (uint32_t) -1;
 | 
					 | 
				
			||||||
	bufferattr.prebuf = (uint32_t) -1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	latency_bytes = pa_usec_to_bytes(impl->latency_msec * SPA_USEC_PER_MSEC, &ss);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	impl->target_latency = latency_bytes / impl->frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* half in our buffer, half in the network + remote */
 | 
					 | 
				
			||||||
	impl->target_buffer = latency_bytes / 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (impl->mode == MODE_SOURCE) {
 | 
					 | 
				
			||||||
		bufferattr.fragsize = latency_bytes / 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		pa_context_subscribe(impl->pa_context,
 | 
					 | 
				
			||||||
				PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, NULL, impl);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		res = pa_stream_connect_record(impl->pa_stream,
 | 
					 | 
				
			||||||
				remote_node_target, &bufferattr,
 | 
					 | 
				
			||||||
				PA_STREAM_DONT_MOVE |
 | 
					 | 
				
			||||||
				PA_STREAM_INTERPOLATE_TIMING |
 | 
					 | 
				
			||||||
				PA_STREAM_ADJUST_LATENCY |
 | 
					 | 
				
			||||||
				PA_STREAM_AUTO_TIMING_UPDATE);
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		bufferattr.tlength = latency_bytes / 2;
 | 
					 | 
				
			||||||
		bufferattr.minreq = bufferattr.tlength / 4;
 | 
					 | 
				
			||||||
		bufferattr.prebuf = bufferattr.tlength;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		pa_context_subscribe(impl->pa_context,
 | 
					 | 
				
			||||||
				PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, impl);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		res = pa_stream_connect_playback(impl->pa_stream,
 | 
					 | 
				
			||||||
				remote_node_target, &bufferattr,
 | 
					 | 
				
			||||||
				PA_STREAM_DONT_MOVE |
 | 
					 | 
				
			||||||
				PA_STREAM_INTERPOLATE_TIMING |
 | 
					 | 
				
			||||||
				PA_STREAM_ADJUST_LATENCY |
 | 
					 | 
				
			||||||
				PA_STREAM_AUTO_TIMING_UPDATE,
 | 
					 | 
				
			||||||
				NULL, NULL);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (res < 0) {
 | 
					 | 
				
			||||||
		res = pa_context_errno(impl->pa_context);
 | 
					 | 
				
			||||||
		goto error_unlock;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (;;) {
 | 
					 | 
				
			||||||
		pa_stream_state_t state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		state = pa_stream_get_state(impl->pa_stream);
 | 
					 | 
				
			||||||
		if (state == PA_STREAM_READY)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!PA_STREAM_IS_GOOD(state)) {
 | 
					 | 
				
			||||||
			res = pa_context_errno(impl->pa_context);
 | 
					 | 
				
			||||||
			goto error_unlock;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* Wait until the stream is ready */
 | 
					 | 
				
			||||||
		pa_threaded_mainloop_wait(impl->pa_mainloop);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit_unlock:
 | 
				
			||||||
	pa_threaded_mainloop_unlock(impl->pa_mainloop);
 | 
						pa_threaded_mainloop_unlock(impl->pa_mainloop);
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
	return 0;
 | 
						if (err != PA_OK)
 | 
				
			||||||
 | 
							pw_log_error("failed to connect: %s", pa_strerror(err));
 | 
				
			||||||
error_unlock:
 | 
						return err_to_res(err);
 | 
				
			||||||
	pa_threaded_mainloop_unlock(impl->pa_mainloop);
 | 
					 | 
				
			||||||
error:
 | 
					 | 
				
			||||||
	pw_log_error("failed to connect: %s", pa_strerror(res));
 | 
					 | 
				
			||||||
	return err_to_res(res);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
 | 
					static void core_error(void *data, uint32_t id, int seq, int res, const char *message)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct impl *impl = data;
 | 
						struct impl *impl = data;
 | 
				
			||||||
| 
						 | 
					@ -1234,10 +1234,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
 | 
				
			||||||
			&impl->core_listener,
 | 
								&impl->core_listener,
 | 
				
			||||||
			&core_events, impl);
 | 
								&core_events, impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((res = create_pulse_stream(impl)) < 0)
 | 
						if ((res = start_pulse_connection(impl)) < 0)
 | 
				
			||||||
		goto error;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((res = create_stream(impl)) < 0)
 | 
					 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
 | 
						pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue