mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-10-29 05:40:27 -04:00 
			
		
		
		
	modules: port modules to timer-queue
Instead of using timerfd, use the context timer-queue to schedule timeouts. This saves fds and removes some redundant code. Make the rtp-source timeout and standby code a bit better by using atomic operations.
This commit is contained in:
		
							parent
							
								
									b220f85790
								
							
						
					
					
						commit
						a75cea96fb
					
				
					 11 changed files with 157 additions and 238 deletions
				
			
		|  | @ -41,6 +41,7 @@ struct pw_avb *pw_avb_new(struct pw_context *context, | ||||||
| 
 | 
 | ||||||
| 	impl->context = context; | 	impl->context = context; | ||||||
| 	impl->loop = pw_context_get_main_loop(context); | 	impl->loop = pw_context_get_main_loop(context); | ||||||
|  | 	impl->timer_queue = pw_context_get_timer_queue(context); | ||||||
| 	impl->props = props; | 	impl->props = props; | ||||||
| 	impl->core = pw_context_get_object(context, PW_TYPE_INTERFACE_Core); | 	impl->core = pw_context_get_object(context, PW_TYPE_INTERFACE_Core); | ||||||
| 	if (impl->core == NULL) { | 	if (impl->core == NULL) { | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <spa/support/cpu.h> | #include <spa/support/cpu.h> | ||||||
| #include <spa/debug/mem.h> | #include <spa/debug/mem.h> | ||||||
|  | #include <spa/utils/result.h> | ||||||
| 
 | 
 | ||||||
| #include <pipewire/pipewire.h> | #include <pipewire/pipewire.h> | ||||||
| 
 | 
 | ||||||
|  | @ -39,12 +40,18 @@ | ||||||
| #define server_emit_periodic(s,n)	server_emit(s, periodic, 0, n) | #define server_emit_periodic(s,n)	server_emit(s, periodic, 0, n) | ||||||
| #define server_emit_command(s,n,c,a,f)	server_emit(s, command, 0, n, c, a, f) | #define server_emit_command(s,n,c,a,f)	server_emit(s, command, 0, n, c, a, f) | ||||||
| 
 | 
 | ||||||
| static void on_timer_event(void *data, uint64_t expirations) | static void on_timer_event(void *data) | ||||||
| { | { | ||||||
| 	struct server *server = data; | 	struct server *server = data; | ||||||
|  | 	struct impl *impl = server->impl; | ||||||
| 	struct timespec now; | 	struct timespec now; | ||||||
|  | 
 | ||||||
| 	clock_gettime(CLOCK_REALTIME, &now); | 	clock_gettime(CLOCK_REALTIME, &now); | ||||||
| 	server_emit_periodic(server, SPA_TIMESPEC_TO_NSEC(&now)); | 	server_emit_periodic(server, SPA_TIMESPEC_TO_NSEC(&now)); | ||||||
|  | 
 | ||||||
|  | 	pw_timer_queue_add(impl->timer_queue, &server->timer, | ||||||
|  | 		&server->timer.timeout, DEFAULT_INTERVAL * SPA_NSEC_PER_SEC, | ||||||
|  | 		on_timer_event, server); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void on_socket_data(void *data, int fd, uint32_t mask) | static void on_socket_data(void *data, int fd, uint32_t mask) | ||||||
|  | @ -201,7 +208,6 @@ static int setup_socket(struct server *server) | ||||||
| 	struct impl *impl = server->impl; | 	struct impl *impl = server->impl; | ||||||
| 	int fd, res; | 	int fd, res; | ||||||
| 	static const uint8_t bmac[6] = AVB_BROADCAST_MAC; | 	static const uint8_t bmac[6] = AVB_BROADCAST_MAC; | ||||||
| 	struct timespec value, interval; |  | ||||||
| 
 | 
 | ||||||
| 	fd = avb_server_make_socket(server, AVB_TSN_ETH, bmac); | 	fd = avb_server_make_socket(server, AVB_TSN_ETH, bmac); | ||||||
| 	if (fd < 0) | 	if (fd < 0) | ||||||
|  | @ -215,18 +221,13 @@ static int setup_socket(struct server *server) | ||||||
| 		pw_log_error("server %p: can't create server source: %m", impl); | 		pw_log_error("server %p: can't create server source: %m", impl); | ||||||
| 		goto error_no_source; | 		goto error_no_source; | ||||||
| 	} | 	} | ||||||
| 	server->timer = pw_loop_add_timer(impl->loop, on_timer_event, server); | 
 | ||||||
| 	if (server->timer == NULL) { | 	if ((res = pw_timer_queue_add(impl->timer_queue, &server->timer, | ||||||
| 		res = -errno; | 			NULL, DEFAULT_INTERVAL * SPA_NSEC_PER_SEC, | ||||||
| 		pw_log_error("server %p: can't create timer source: %m", impl); | 			on_timer_event, server)) < 0) { | ||||||
|  | 		pw_log_error("server %p: can't create timer: %s", impl, spa_strerror(res)); | ||||||
| 		goto error_no_timer; | 		goto error_no_timer; | ||||||
| 	} | 	} | ||||||
| 	value.tv_sec = 0; |  | ||||||
| 	value.tv_nsec = 1; |  | ||||||
| 	interval.tv_sec = DEFAULT_INTERVAL; |  | ||||||
| 	interval.tv_nsec = 0; |  | ||||||
| 	pw_loop_update_timer(impl->loop, server->timer, &value, &interval, false); |  | ||||||
| 
 |  | ||||||
| 	return 0; | 	return 0; | ||||||
| 
 | 
 | ||||||
| error_no_timer: | error_no_timer: | ||||||
|  | @ -310,8 +311,7 @@ void avdecc_server_free(struct server *server) | ||||||
| 	spa_list_remove(&server->link); | 	spa_list_remove(&server->link); | ||||||
| 	if (server->source) | 	if (server->source) | ||||||
| 		pw_loop_destroy_source(impl->loop, server->source); | 		pw_loop_destroy_source(impl->loop, server->source); | ||||||
| 	if (server->timer) | 	pw_timer_queue_cancel(&server->timer); | ||||||
| 		pw_loop_destroy_source(impl->loop, server->timer); |  | ||||||
| 	spa_hook_list_clean(&server->listener_list); | 	spa_hook_list_clean(&server->listener_list); | ||||||
| 	free(server); | 	free(server); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ struct avb_mrp; | ||||||
| 
 | 
 | ||||||
| struct impl { | struct impl { | ||||||
| 	struct pw_loop *loop; | 	struct pw_loop *loop; | ||||||
|  | 	struct pw_timer_queue *timer_queue; | ||||||
| 	struct pw_context *context; | 	struct pw_context *context; | ||||||
| 	struct spa_hook context_listener; | 	struct spa_hook context_listener; | ||||||
| 	struct pw_core *core; | 	struct pw_core *core; | ||||||
|  | @ -61,7 +62,7 @@ struct server { | ||||||
| 	int ifindex; | 	int ifindex; | ||||||
| 
 | 
 | ||||||
| 	struct spa_source *source; | 	struct spa_source *source; | ||||||
| 	struct spa_source *timer; | 	struct pw_timer timer; | ||||||
| 
 | 
 | ||||||
| 	struct spa_hook_list listener_list; | 	struct spa_hook_list listener_list; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -225,6 +225,7 @@ struct impl { | ||||||
| 	struct pw_loop *main_loop; | 	struct pw_loop *main_loop; | ||||||
| 	struct pw_loop *data_loop; | 	struct pw_loop *data_loop; | ||||||
| 	struct spa_system *system; | 	struct spa_system *system; | ||||||
|  | 	struct pw_timer_queue *timer_queue; | ||||||
| 
 | 
 | ||||||
| #define MODE_SINK	(1<<0) | #define MODE_SINK	(1<<0) | ||||||
| #define MODE_SOURCE	(1<<1) | #define MODE_SOURCE	(1<<1) | ||||||
|  | @ -263,7 +264,7 @@ struct impl { | ||||||
| 
 | 
 | ||||||
| 	struct spa_source *setup_socket; | 	struct spa_source *setup_socket; | ||||||
| 	struct spa_source *socket; | 	struct spa_source *socket; | ||||||
| 	struct spa_source *timer; | 	struct pw_timer timer; | ||||||
| 	int32_t init_retry; | 	int32_t init_retry; | ||||||
| 
 | 
 | ||||||
| 	struct netjack2_peer peer; | 	struct netjack2_peer peer; | ||||||
|  | @ -801,14 +802,14 @@ error: | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void on_timer_event(void *data); | ||||||
|  | 
 | ||||||
| static void update_timer(struct impl *impl, uint64_t timeout) | static void update_timer(struct impl *impl, uint64_t timeout) | ||||||
| { | { | ||||||
| 	struct timespec value, interval; | 	pw_timer_queue_cancel(&impl->timer); | ||||||
| 	value.tv_sec = 0; | 	pw_timer_queue_add(impl->timer_queue, &impl->timer, | ||||||
| 	value.tv_nsec = timeout ? 1 : 0; | 			NULL, timeout * SPA_NSEC_PER_SEC, | ||||||
| 	interval.tv_sec = timeout; | 			on_timer_event, impl); | ||||||
| 	interval.tv_nsec = 0; |  | ||||||
| 	pw_loop_update_timer(impl->main_loop, impl->timer, &value, &interval, false); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool encoding_supported(uint32_t encoder) | static bool encoding_supported(uint32_t encoder) | ||||||
|  | @ -1132,7 +1133,7 @@ static void restart_netjack2_socket(struct impl *impl) | ||||||
| 	create_netjack2_socket(impl); | 	create_netjack2_socket(impl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void on_timer_event(void *data, uint64_t expirations) | static void on_timer_event(void *data) | ||||||
| { | { | ||||||
| 	struct impl *impl = data; | 	struct impl *impl = data; | ||||||
| 
 | 
 | ||||||
|  | @ -1193,8 +1194,7 @@ static void impl_destroy(struct impl *impl) | ||||||
| 	if (impl->core && impl->do_disconnect) | 	if (impl->core && impl->do_disconnect) | ||||||
| 		pw_core_disconnect(impl->core); | 		pw_core_disconnect(impl->core); | ||||||
| 
 | 
 | ||||||
| 	if (impl->timer) | 	pw_timer_queue_cancel(&impl->timer); | ||||||
| 		pw_loop_destroy_source(impl->main_loop, impl->timer); |  | ||||||
| 
 | 
 | ||||||
| 	if (impl->data_loop) | 	if (impl->data_loop) | ||||||
| 		pw_context_release_loop(impl->context, impl->data_loop); | 		pw_context_release_loop(impl->context, impl->data_loop); | ||||||
|  | @ -1283,6 +1283,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	impl->main_loop = pw_context_get_main_loop(context); | 	impl->main_loop = pw_context_get_main_loop(context); | ||||||
|  | 	impl->timer_queue = pw_context_get_timer_queue(context); | ||||||
| 	impl->system = impl->main_loop->system; | 	impl->system = impl->main_loop->system; | ||||||
| 
 | 
 | ||||||
| 	impl->source.impl = impl; | 	impl->source.impl = impl; | ||||||
|  | @ -1370,13 +1371,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 			&impl->core_listener, | 			&impl->core_listener, | ||||||
| 			&core_events, impl); | 			&core_events, impl); | ||||||
| 
 | 
 | ||||||
| 	impl->timer = pw_loop_add_timer(impl->main_loop, on_timer_event, impl); |  | ||||||
| 	if (impl->timer == NULL) { |  | ||||||
| 		res = -errno; |  | ||||||
| 		pw_log_error("can't create timer source: %m"); |  | ||||||
| 		goto error; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if ((res = create_netjack2_socket(impl)) < 0) | 	if ((res = create_netjack2_socket(impl)) < 0) | ||||||
| 		goto error; | 		goto error; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -71,7 +71,7 @@ struct module_combine_sink_data { | ||||||
| 	struct pw_properties *combine_props; | 	struct pw_properties *combine_props; | ||||||
| 	struct pw_properties *stream_props; | 	struct pw_properties *stream_props; | ||||||
| 
 | 
 | ||||||
| 	struct spa_source *sinks_timeout; | 	struct pw_timer sinks_timeout; | ||||||
| 
 | 
 | ||||||
| 	unsigned int sinks_pending; | 	unsigned int sinks_pending; | ||||||
| 	unsigned int load_emitted:1; | 	unsigned int load_emitted:1; | ||||||
|  | @ -128,7 +128,7 @@ static const struct pw_manager_events manager_events = { | ||||||
| 	.added = manager_added, | 	.added = manager_added, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void on_sinks_timeout(void *d, uint64_t count) | static void on_sinks_timeout(void *d) | ||||||
| { | { | ||||||
| 	struct module_combine_sink_data *data = d; | 	struct module_combine_sink_data *data = d; | ||||||
| 
 | 
 | ||||||
|  | @ -214,13 +214,10 @@ static int module_combine_sink_load(struct module *module) | ||||||
| 	pw_manager_add_listener(data->manager, &data->manager_listener, | 	pw_manager_add_listener(data->manager, &data->manager_listener, | ||||||
| 			&manager_events, data); | 			&manager_events, data); | ||||||
| 
 | 
 | ||||||
| 	data->sinks_timeout = pw_loop_add_timer(module->impl->main_loop, on_sinks_timeout, data); | 	pw_timer_queue_add(module->impl->timer_queue, &data->sinks_timeout, | ||||||
| 	if (data->sinks_timeout) { | 			NULL, TIMEOUT_SINKS_MSEC * SPA_NSEC_PER_MSEC, | ||||||
| 		struct timespec timeout = {0}; | 			on_sinks_timeout, data); | ||||||
| 		timeout.tv_sec = TIMEOUT_SINKS_MSEC / 1000; | 
 | ||||||
| 		timeout.tv_nsec = (TIMEOUT_SINKS_MSEC % 1000) * SPA_NSEC_PER_MSEC; |  | ||||||
| 		pw_loop_update_timer(module->impl->main_loop, data->sinks_timeout, &timeout, NULL, false); |  | ||||||
| 	} |  | ||||||
| 	return data->load_emitted ? 0 : SPA_RESULT_RETURN_ASYNC(0); | 	return data->load_emitted ? 0 : SPA_RESULT_RETURN_ASYNC(0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -228,8 +225,7 @@ static int module_combine_sink_unload(struct module *module) | ||||||
| { | { | ||||||
| 	struct module_combine_sink_data *d = module->user_data; | 	struct module_combine_sink_data *d = module->user_data; | ||||||
| 
 | 
 | ||||||
| 	if (d->sinks_timeout != NULL) | 	pw_timer_queue_cancel(&d->sinks_timeout); | ||||||
| 		pw_loop_destroy_source(module->impl->main_loop, d->sinks_timeout); |  | ||||||
| 
 | 
 | ||||||
| 	if (d->mod != NULL) { | 	if (d->mod != NULL) { | ||||||
| 		spa_hook_remove(&d->mod_listener); | 		spa_hook_remove(&d->mod_listener); | ||||||
|  |  | ||||||
|  | @ -148,6 +148,7 @@ static const struct spa_dict_item module_props[] = { | ||||||
| struct impl { | struct impl { | ||||||
| 	struct pw_context *context; | 	struct pw_context *context; | ||||||
| 	struct pw_loop *main_loop; | 	struct pw_loop *main_loop; | ||||||
|  | 	struct pw_timer_queue *timer_queue; | ||||||
| 
 | 
 | ||||||
| #define MODE_SINK	0 | #define MODE_SINK	0 | ||||||
| #define MODE_SOURCE	1 | #define MODE_SOURCE	1 | ||||||
|  | @ -194,7 +195,7 @@ struct impl { | ||||||
| 	bool do_disconnect:1; | 	bool do_disconnect:1; | ||||||
| 	bool stopping; | 	bool stopping; | ||||||
| 
 | 
 | ||||||
| 	struct spa_source *timer; | 	struct pw_timer timer; | ||||||
| 	uint32_t reconnect_interval_ms; | 	uint32_t reconnect_interval_ms; | ||||||
| 	bool recovering; | 	bool recovering; | ||||||
| }; | }; | ||||||
|  | @ -525,7 +526,7 @@ static void cleanup_streams(struct impl *impl) | ||||||
| 		pw_stream_destroy(impl->stream); | 		pw_stream_destroy(impl->stream); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void on_timer_event(void *data, uint64_t expirations) | static void on_timer_event(void *data) | ||||||
| { | { | ||||||
| 	struct impl *impl = data; | 	struct impl *impl = data; | ||||||
| 	cleanup_streams(impl); | 	cleanup_streams(impl); | ||||||
|  | @ -538,13 +539,9 @@ do_schedule_recovery(struct spa_loop *loop, | ||||||
| { | { | ||||||
| 	struct impl *impl = user_data; | 	struct impl *impl = user_data; | ||||||
| 	if (impl->reconnect_interval_ms > 0) { | 	if (impl->reconnect_interval_ms > 0) { | ||||||
| 		struct timespec value; | 		pw_timer_queue_add(impl->timer_queue, &impl->timer, | ||||||
| 		uint64_t timestamp; | 				NULL, impl->reconnect_interval_ms * SPA_NSEC_PER_MSEC, | ||||||
| 
 | 				on_timer_event, impl); | ||||||
| 		timestamp = impl->reconnect_interval_ms * SPA_NSEC_PER_MSEC; |  | ||||||
| 		value.tv_sec = timestamp / SPA_NSEC_PER_SEC; |  | ||||||
| 		value.tv_nsec = timestamp % SPA_NSEC_PER_SEC; |  | ||||||
| 		pw_loop_update_timer(impl->main_loop, impl->timer, &value, NULL, false); |  | ||||||
| 	} else { | 	} else { | ||||||
| 		if (impl->module) | 		if (impl->module) | ||||||
| 			pw_impl_module_schedule_destroy(impl->module); | 			pw_impl_module_schedule_destroy(impl->module); | ||||||
|  | @ -1029,8 +1026,7 @@ static void impl_destroy(struct impl *impl) | ||||||
| 	pw_properties_free(impl->stream_props); | 	pw_properties_free(impl->stream_props); | ||||||
| 	pw_properties_free(impl->props); | 	pw_properties_free(impl->props); | ||||||
| 
 | 
 | ||||||
| 	if (impl->timer) | 	pw_timer_queue_cancel(&impl->timer); | ||||||
| 		pw_loop_destroy_source(impl->main_loop, impl->timer); |  | ||||||
| 
 | 
 | ||||||
| 	free(impl->buffer); | 	free(impl->buffer); | ||||||
| 	free(impl); | 	free(impl); | ||||||
|  | @ -1144,6 +1140,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	impl->module = module; | 	impl->module = module; | ||||||
| 	impl->context = context; | 	impl->context = context; | ||||||
| 	impl->main_loop = pw_context_get_main_loop(context); | 	impl->main_loop = pw_context_get_main_loop(context); | ||||||
|  | 	impl->timer_queue = pw_context_get_timer_queue(context); | ||||||
| 
 | 
 | ||||||
| 	spa_ringbuffer_init(&impl->ring); | 	spa_ringbuffer_init(&impl->ring); | ||||||
| 	impl->buffer = calloc(1, RINGBUFFER_SIZE); | 	impl->buffer = calloc(1, RINGBUFFER_SIZE); | ||||||
|  | @ -1165,13 +1162,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	impl->reconnect_interval_ms = pw_properties_get_uint32(props, | 	impl->reconnect_interval_ms = pw_properties_get_uint32(props, | ||||||
| 			"reconnect.interval.ms", 0); | 			"reconnect.interval.ms", 0); | ||||||
| 
 | 
 | ||||||
| 	impl->timer = pw_loop_add_timer(impl->main_loop, on_timer_event, impl); |  | ||||||
| 	if (impl->timer == NULL) { |  | ||||||
| 		res = -errno; |  | ||||||
| 		pw_log_error("can't create timer source: %m"); |  | ||||||
| 		goto error; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	impl->latency_msec = pw_properties_get_uint32(props, "pulse.latency", DEFAULT_LATENCY_MSEC); | 	impl->latency_msec = pw_properties_get_uint32(props, "pulse.latency", DEFAULT_LATENCY_MSEC); | ||||||
| 
 | 
 | ||||||
| 	if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) | 	if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) | ||||||
|  |  | ||||||
|  | @ -212,6 +212,7 @@ struct impl { | ||||||
| 
 | 
 | ||||||
| 	struct pw_impl_module *module; | 	struct pw_impl_module *module; | ||||||
| 	struct pw_loop *loop; | 	struct pw_loop *loop; | ||||||
|  | 	struct pw_timer_queue *timer_queue; | ||||||
| 
 | 
 | ||||||
| 	struct spa_hook module_listener; | 	struct spa_hook module_listener; | ||||||
| 
 | 
 | ||||||
|  | @ -245,7 +246,7 @@ struct impl { | ||||||
| 	uint16_t control_port; | 	uint16_t control_port; | ||||||
| 	int control_fd; | 	int control_fd; | ||||||
| 	struct spa_source *control_source; | 	struct spa_source *control_source; | ||||||
| 	struct spa_source *feedback_timer; | 	struct pw_timer feedback_timer; | ||||||
| 
 | 
 | ||||||
| 	uint16_t timing_port; | 	uint16_t timing_port; | ||||||
| 	int timing_fd; | 	int timing_fd; | ||||||
|  | @ -836,12 +837,16 @@ static int rtsp_send_volume(struct impl *impl) | ||||||
| 	return rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, rtsp_log_reply_status); | 	return rtsp_send(impl, "SET_PARAMETER", "text/parameters", header, rtsp_log_reply_status); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void rtsp_do_post_feedback(void *data, uint64_t expirations) | static void rtsp_do_post_feedback(void *data) | ||||||
| { | { | ||||||
| 	struct impl *impl = data; | 	struct impl *impl = data; | ||||||
| 
 | 
 | ||||||
| 	pw_rtsp_client_url_send(impl->rtsp, "/feedback", "POST", &impl->headers->dict, | 	pw_rtsp_client_url_send(impl->rtsp, "/feedback", "POST", &impl->headers->dict, | ||||||
| 					NULL, NULL, 0, rtsp_log_reply_status, impl); | 					NULL, NULL, 0, rtsp_log_reply_status, impl); | ||||||
|  | 
 | ||||||
|  | 	pw_timer_queue_add(impl->timer_queue, &impl->feedback_timer, | ||||||
|  | 			&impl->feedback_timer.timeout, 2 * SPA_NSEC_PER_SEC, | ||||||
|  | 			rtsp_do_post_feedback, impl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint32_t msec_to_samples(struct impl *impl, uint32_t msec) | static uint32_t msec_to_samples(struct impl *impl, uint32_t msec) | ||||||
|  | @ -854,7 +859,6 @@ static int rtsp_record_reply(void *data, int status, const struct spa_dict *head | ||||||
| 	struct impl *impl = data; | 	struct impl *impl = data; | ||||||
| 	const char *str; | 	const char *str; | ||||||
| 	char progress[128]; | 	char progress[128]; | ||||||
| 	struct timespec timeout, interval; |  | ||||||
| 	struct spa_process_latency_info process_latency; | 	struct spa_process_latency_info process_latency; | ||||||
| 
 | 
 | ||||||
| 	pw_log_info("record status: %d", status); | 	pw_log_info("record status: %d", status); | ||||||
|  | @ -866,16 +870,12 @@ static int rtsp_record_reply(void *data, int status, const struct spa_dict *head | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	timeout.tv_sec = 2; |  | ||||||
| 	timeout.tv_nsec = 0; |  | ||||||
| 	interval.tv_sec = 2; |  | ||||||
| 	interval.tv_nsec = 0; |  | ||||||
| 
 |  | ||||||
| 	// feedback timer is only needed for auth_setup	encryption
 | 	// feedback timer is only needed for auth_setup	encryption
 | ||||||
| 	if (impl->encryption == CRYPTO_FP_SAP25) { | 	if (impl->encryption == CRYPTO_FP_SAP25) { | ||||||
| 		if (!impl->feedback_timer) | 		pw_timer_queue_cancel(&impl->feedback_timer); | ||||||
| 			impl->feedback_timer = pw_loop_add_timer(impl->loop, rtsp_do_post_feedback, impl); | 		pw_timer_queue_add(impl->timer_queue, &impl->feedback_timer, | ||||||
| 		pw_loop_update_timer(impl->loop, impl->feedback_timer, &timeout, &interval, false); | 				NULL, 2 * SPA_NSEC_PER_SEC, | ||||||
|  | 				rtsp_do_post_feedback, impl); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ((str = spa_dict_lookup(headers, "Audio-Latency")) != NULL) { | 	if ((str = spa_dict_lookup(headers, "Audio-Latency")) != NULL) { | ||||||
|  | @ -1476,10 +1476,8 @@ static void connection_cleanup(struct impl *impl) | ||||||
| 		close(impl->timing_fd); | 		close(impl->timing_fd); | ||||||
| 		impl->timing_fd = -1; | 		impl->timing_fd = -1; | ||||||
| 	} | 	} | ||||||
| 	if (impl->feedback_timer != NULL) { | 	pw_timer_queue_cancel(&impl->feedback_timer); | ||||||
| 		pw_loop_destroy_source(impl->loop, impl->feedback_timer); | 
 | ||||||
| 		impl->feedback_timer = NULL; |  | ||||||
| 	} |  | ||||||
| 	free(impl->auth_method); | 	free(impl->auth_method); | ||||||
| 	impl->auth_method = NULL; | 	impl->auth_method = NULL; | ||||||
| 	free(impl->realm); | 	free(impl->realm); | ||||||
|  | @ -1792,6 +1790,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	impl->module = module; | 	impl->module = module; | ||||||
| 	impl->context = context; | 	impl->context = context; | ||||||
| 	impl->loop = pw_context_get_main_loop(context); | 	impl->loop = pw_context_get_main_loop(context); | ||||||
|  | 	impl->timer_queue = pw_context_get_timer_queue(context); | ||||||
| 
 | 
 | ||||||
| 	ip = pw_properties_get(props, "raop.ip"); | 	ip = pw_properties_get(props, "raop.ip"); | ||||||
| 	port = pw_properties_get(props, "raop.port"); | 	port = pw_properties_get(props, "raop.port"); | ||||||
|  |  | ||||||
|  | @ -251,6 +251,7 @@ struct impl { | ||||||
| 	struct pw_properties *props; | 	struct pw_properties *props; | ||||||
| 
 | 
 | ||||||
| 	struct pw_loop *loop; | 	struct pw_loop *loop; | ||||||
|  | 	struct pw_timer_queue *timer_queue; | ||||||
| 
 | 
 | ||||||
| 	struct pw_impl_module *module; | 	struct pw_impl_module *module; | ||||||
| 	struct spa_hook module_listener; | 	struct spa_hook module_listener; | ||||||
|  | @ -263,7 +264,7 @@ struct impl { | ||||||
| 	struct pw_registry *registry; | 	struct pw_registry *registry; | ||||||
| 	struct spa_hook registry_listener; | 	struct spa_hook registry_listener; | ||||||
| 
 | 
 | ||||||
| 	struct spa_source *timer; | 	struct pw_timer timer; | ||||||
| 
 | 
 | ||||||
| 	char *ifname; | 	char *ifname; | ||||||
| 	uint32_t ttl; | 	uint32_t ttl; | ||||||
|  | @ -920,7 +921,7 @@ static int send_sap(struct impl *impl, struct session *sess, bool bye) | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void on_timer_event(void *data, uint64_t expirations) | static void on_timer_event(void *data) | ||||||
| { | { | ||||||
| 	struct impl *impl = data; | 	struct impl *impl = data; | ||||||
| 	struct session *sess, *tmp; | 	struct session *sess, *tmp; | ||||||
|  | @ -954,6 +955,9 @@ static void on_timer_event(void *data, uint64_t expirations) | ||||||
| 
 | 
 | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	pw_timer_queue_add(impl->timer_queue, &impl->timer, | ||||||
|  | 			&impl->timer.timeout, SAP_INTERVAL_SEC * SPA_NSEC_PER_SEC, | ||||||
|  | 			on_timer_event, impl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct session *session_find(struct impl *impl, const struct sdp_info *info) | static struct session *session_find(struct impl *impl, const struct sdp_info *info) | ||||||
|  | @ -1647,22 +1651,15 @@ on_sap_io(void *data, int fd, uint32_t mask) | ||||||
| static int start_sap(struct impl *impl) | static int start_sap(struct impl *impl) | ||||||
| { | { | ||||||
| 	int fd = -1, res; | 	int fd = -1, res; | ||||||
| 	struct timespec value, interval; |  | ||||||
| 	char addr[128] = "invalid"; | 	char addr[128] = "invalid"; | ||||||
| 
 | 
 | ||||||
| 	pw_log_info("starting SAP timer"); | 	pw_log_info("starting SAP timer"); | ||||||
| 	impl->timer = pw_loop_add_timer(impl->loop, on_timer_event, impl); | 	if ((res = pw_timer_queue_add(impl->timer_queue, &impl->timer, | ||||||
| 	if (impl->timer == NULL) { | 			NULL, SAP_INTERVAL_SEC * SPA_NSEC_PER_SEC, | ||||||
| 		res = -errno; | 			on_timer_event, impl)) < 0) { | ||||||
| 		pw_log_error("can't create timer source: %m"); | 		pw_log_error("can't add timer: %s", spa_strerror(res)); | ||||||
| 		goto error; | 		goto error; | ||||||
| 	} | 	} | ||||||
| 	value.tv_sec = 0; |  | ||||||
| 	value.tv_nsec = 1; |  | ||||||
| 	interval.tv_sec = SAP_INTERVAL_SEC; |  | ||||||
| 	interval.tv_nsec = 0; |  | ||||||
| 	pw_loop_update_timer(impl->loop, impl->timer, &value, &interval, false); |  | ||||||
| 
 |  | ||||||
| 	if ((fd = make_recv_socket(&impl->sap_addr, impl->sap_len, impl->ifname)) < 0) | 	if ((fd = make_recv_socket(&impl->sap_addr, impl->sap_len, impl->ifname)) < 0) | ||||||
| 		return fd; | 		return fd; | ||||||
| 
 | 
 | ||||||
|  | @ -1809,8 +1806,7 @@ static void impl_destroy(struct impl *impl) | ||||||
| 	if (impl->core && impl->do_disconnect) | 	if (impl->core && impl->do_disconnect) | ||||||
| 		pw_core_disconnect(impl->core); | 		pw_core_disconnect(impl->core); | ||||||
| 
 | 
 | ||||||
| 	if (impl->timer) | 	pw_timer_queue_cancel(&impl->timer); | ||||||
| 		pw_loop_destroy_source(impl->loop, impl->timer); |  | ||||||
| 	if (impl->sap_source) | 	if (impl->sap_source) | ||||||
| 		pw_loop_destroy_source(impl->loop, impl->sap_source); | 		pw_loop_destroy_source(impl->loop, impl->sap_source); | ||||||
| 
 | 
 | ||||||
|  | @ -1890,6 +1886,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 
 | 
 | ||||||
| 	impl->module = module; | 	impl->module = module; | ||||||
| 	impl->loop = pw_context_get_main_loop(context); | 	impl->loop = pw_context_get_main_loop(context); | ||||||
|  | 	impl->timer_queue = pw_context_get_timer_queue(context); | ||||||
| 
 | 
 | ||||||
| 	str = pw_properties_get(props, "local.ifname"); | 	str = pw_properties_get(props, "local.ifname"); | ||||||
| 	impl->ifname = str ? strdup(str) : NULL; | 	impl->ifname = str ? strdup(str) : NULL; | ||||||
|  |  | ||||||
|  | @ -204,7 +204,7 @@ struct session { | ||||||
| #define SESSION_STATE_ESTABLISHED	4 | #define SESSION_STATE_ESTABLISHED	4 | ||||||
| 	int state; | 	int state; | ||||||
| 	int ck_count; | 	int ck_count; | ||||||
| 	uint64_t next_time; | 	struct pw_timer timer; | ||||||
| 
 | 
 | ||||||
| 	uint32_t ctrl_initiator; | 	uint32_t ctrl_initiator; | ||||||
| 	uint32_t data_initiator; | 	uint32_t data_initiator; | ||||||
|  | @ -237,15 +237,13 @@ struct impl { | ||||||
| 
 | 
 | ||||||
| 	struct pw_loop *loop; | 	struct pw_loop *loop; | ||||||
| 	struct pw_loop *data_loop; | 	struct pw_loop *data_loop; | ||||||
|  | 	struct pw_timer_queue *timer_queue; | ||||||
| 
 | 
 | ||||||
| 	struct pw_core *core; | 	struct pw_core *core; | ||||||
| 	struct spa_hook core_listener; | 	struct spa_hook core_listener; | ||||||
| 	struct spa_hook core_proxy_listener; | 	struct spa_hook core_proxy_listener; | ||||||
| 	unsigned int do_disconnect:1; | 	unsigned int do_disconnect:1; | ||||||
| 
 | 
 | ||||||
| 	struct spa_source *timer; |  | ||||||
| 	uint64_t next_time; |  | ||||||
| 
 |  | ||||||
| 	struct spa_source *ctrl_source; | 	struct spa_source *ctrl_source; | ||||||
| 	struct spa_source *data_source; | 	struct spa_source *data_source; | ||||||
| 
 | 
 | ||||||
|  | @ -276,34 +274,19 @@ static ssize_t send_packet(int fd, struct msghdr *msg) | ||||||
| 	return n; | 	return n; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint64_t current_time_ns(void) | static uint64_t current_time_ns(struct timespec *ts) | ||||||
| { | { | ||||||
| 	struct timespec ts; | 	clock_gettime(CLOCK_MONOTONIC, ts); | ||||||
| 	clock_gettime(CLOCK_MONOTONIC, &ts); | 	return SPA_TIMESPEC_TO_NSEC(ts); | ||||||
| 	return SPA_TIMESPEC_TO_NSEC(&ts); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void set_timeout(struct impl *impl, uint64_t time) | static void send_apple_midi_cmd_ck0(struct session *sess); | ||||||
| { |  | ||||||
| 	struct itimerspec ts; |  | ||||||
| 	ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; |  | ||||||
| 	ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; |  | ||||||
| 	ts.it_interval.tv_sec = 0; |  | ||||||
| 	ts.it_interval.tv_nsec = 0; |  | ||||||
| 	pw_loop_update_timer(impl->loop, impl->timer, &ts.it_value, &ts.it_interval, true); |  | ||||||
| 	impl->next_time = time; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| static void schedule_timeout(struct impl *impl) | static void on_timer_event(void *data) | ||||||
| { | { | ||||||
| 	struct session *sess; | 	struct session *sess = data; | ||||||
| 	uint64_t next_time = 0; | 	pw_log_debug("timeout"); | ||||||
| 	spa_list_for_each(sess, &impl->sessions, link) { | 	send_apple_midi_cmd_ck0(sess); | ||||||
| 		if (next_time == 0 || |  | ||||||
| 		    (sess->next_time != 0 && sess->next_time < next_time)) |  | ||||||
| 			next_time = sess->next_time; |  | ||||||
| 	} |  | ||||||
| 	set_timeout(impl, next_time); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void send_apple_midi_cmd_ck0(struct session *sess) | static void send_apple_midi_cmd_ck0(struct session *sess) | ||||||
|  | @ -312,14 +295,14 @@ static void send_apple_midi_cmd_ck0(struct session *sess) | ||||||
| 	struct iovec iov[3]; | 	struct iovec iov[3]; | ||||||
| 	struct msghdr msg; | 	struct msghdr msg; | ||||||
| 	struct rtp_apple_midi_ck hdr; | 	struct rtp_apple_midi_ck hdr; | ||||||
| 	uint64_t current_time, ts; | 	struct timespec now; | ||||||
|  | 	uint64_t timeout, ts; | ||||||
| 
 | 
 | ||||||
| 	spa_zero(hdr); | 	spa_zero(hdr); | ||||||
| 	hdr.cmd = htonl(APPLE_MIDI_CMD_CK); | 	hdr.cmd = htonl(APPLE_MIDI_CMD_CK); | ||||||
| 	hdr.ssrc = htonl(sess->ssrc); | 	hdr.ssrc = htonl(sess->ssrc); | ||||||
| 
 | 
 | ||||||
| 	current_time = current_time_ns(); | 	ts = current_time_ns(&now) / 10000; | ||||||
| 	ts = current_time / 10000; |  | ||||||
| 	hdr.ts1_h = htonl(ts >> 32); | 	hdr.ts1_h = htonl(ts >> 32); | ||||||
| 	hdr.ts1_l = htonl(ts); | 	hdr.ts1_l = htonl(ts); | ||||||
| 
 | 
 | ||||||
|  | @ -335,11 +318,15 @@ static void send_apple_midi_cmd_ck0(struct session *sess) | ||||||
| 	send_packet(impl->data_source->fd, &msg); | 	send_packet(impl->data_source->fd, &msg); | ||||||
| 
 | 
 | ||||||
| 	if (sess->ck_count++ < 8) | 	if (sess->ck_count++ < 8) | ||||||
| 		sess->next_time = current_time + SPA_NSEC_PER_SEC; | 		timeout = 1; | ||||||
| 	else if (sess->ck_count++ < 16) | 	else if (sess->ck_count++ < 16) | ||||||
| 		sess->next_time = current_time + 2 * SPA_NSEC_PER_SEC; | 		timeout = 2; | ||||||
| 	else | 	else | ||||||
| 		sess->next_time = current_time + 5 * SPA_NSEC_PER_SEC; | 		timeout = 5; | ||||||
|  | 
 | ||||||
|  | 	pw_timer_queue_add(impl->timer_queue, &sess->timer, | ||||||
|  | 			&now, timeout * SPA_NSEC_PER_SEC, | ||||||
|  | 			on_timer_event, sess); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void session_update_state(struct session *sess, int state) | static void session_update_state(struct session *sess, int state) | ||||||
|  | @ -355,12 +342,10 @@ static void session_update_state(struct session *sess, int state) | ||||||
| 		if (sess->we_initiated) { | 		if (sess->we_initiated) { | ||||||
| 			sess->ck_count = 0; | 			sess->ck_count = 0; | ||||||
| 			send_apple_midi_cmd_ck0(sess); | 			send_apple_midi_cmd_ck0(sess); | ||||||
| 			schedule_timeout(sess->impl); |  | ||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
| 	case SESSION_STATE_INIT: | 	case SESSION_STATE_INIT: | ||||||
| 		sess->next_time = 0; | 		pw_timer_queue_cancel(&sess->timer); | ||||||
| 		schedule_timeout(sess->impl); |  | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		break; | 		break; | ||||||
|  | @ -601,6 +586,7 @@ static void free_session(struct session *sess) | ||||||
| 	pw_loop_locked(impl->data_loop, do_unlink_session, 1, NULL, 0, sess); | 	pw_loop_locked(impl->data_loop, do_unlink_session, 1, NULL, 0, sess); | ||||||
| 
 | 
 | ||||||
| 	sess->impl->n_sessions--; | 	sess->impl->n_sessions--; | ||||||
|  | 	pw_timer_queue_cancel(&sess->timer); | ||||||
| 
 | 
 | ||||||
| 	if (sess->send) | 	if (sess->send) | ||||||
| 		rtp_stream_destroy(sess->send); | 		rtp_stream_destroy(sess->send); | ||||||
|  | @ -859,8 +845,9 @@ static void parse_apple_midi_cmd_ck(struct impl *impl, bool ctrl, uint8_t *buffe | ||||||
| 	struct msghdr msg; | 	struct msghdr msg; | ||||||
| 	struct rtp_apple_midi_ck reply; | 	struct rtp_apple_midi_ck reply; | ||||||
| 	struct session *sess; | 	struct session *sess; | ||||||
| 	uint64_t now, t1, t2, t3; | 	uint64_t ts, t1, t2, t3; | ||||||
| 	uint32_t ssrc = ntohl(hdr->ssrc); | 	uint32_t ssrc = ntohl(hdr->ssrc); | ||||||
|  | 	struct timespec now; | ||||||
| 
 | 
 | ||||||
| 	sess = find_session_by_ssrc(impl, ssrc); | 	sess = find_session_by_ssrc(impl, ssrc); | ||||||
| 	if (sess == NULL) { | 	if (sess == NULL) { | ||||||
|  | @ -870,7 +857,7 @@ static void parse_apple_midi_cmd_ck(struct impl *impl, bool ctrl, uint8_t *buffe | ||||||
| 
 | 
 | ||||||
| 	pw_log_trace("got CK count %d", hdr->count); | 	pw_log_trace("got CK count %d", hdr->count); | ||||||
| 
 | 
 | ||||||
| 	now = current_time_ns() / 10000; | 	ts = current_time_ns(&now) / 10000; | ||||||
| 	reply = *hdr; | 	reply = *hdr; | ||||||
| 	reply.ssrc = htonl(sess->ssrc); | 	reply.ssrc = htonl(sess->ssrc); | ||||||
| 	reply.count++; | 	reply.count++; | ||||||
|  | @ -882,11 +869,11 @@ static void parse_apple_midi_cmd_ck(struct impl *impl, bool ctrl, uint8_t *buffe | ||||||
| 
 | 
 | ||||||
| 	switch (hdr->count) { | 	switch (hdr->count) { | ||||||
| 	case 0: | 	case 0: | ||||||
| 		t2 = now; | 		t2 = ts; | ||||||
| 		break; | 		break; | ||||||
| 	case 1: | 	case 1: | ||||||
| 		t2 = ((uint64_t)ntohl(hdr->ts2_h) << 32) | ntohl(hdr->ts2_l); | 		t2 = ((uint64_t)ntohl(hdr->ts2_h) << 32) | ntohl(hdr->ts2_l); | ||||||
| 		t3 = now; | 		t3 = ts; | ||||||
| 		break; | 		break; | ||||||
| 	case 2: | 	case 2: | ||||||
| 		t3 = ((uint64_t)ntohl(hdr->ts3_h) << 32) | ntohl(hdr->ts3_l); | 		t3 = ((uint64_t)ntohl(hdr->ts3_h) << 32) | ntohl(hdr->ts3_l); | ||||||
|  | @ -1223,8 +1210,6 @@ static void impl_destroy(struct impl *impl) | ||||||
| 	if (impl->core && impl->do_disconnect) | 	if (impl->core && impl->do_disconnect) | ||||||
| 		pw_core_disconnect(impl->core); | 		pw_core_disconnect(impl->core); | ||||||
| 
 | 
 | ||||||
| 	if (impl->timer) |  | ||||||
| 		pw_loop_destroy_source(impl->loop, impl->timer); |  | ||||||
| 	if (impl->ctrl_source) | 	if (impl->ctrl_source) | ||||||
| 		pw_loop_destroy_source(impl->loop, impl->ctrl_source); | 		pw_loop_destroy_source(impl->loop, impl->ctrl_source); | ||||||
| 	if (impl->data_source) | 	if (impl->data_source) | ||||||
|  | @ -1646,24 +1631,6 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void on_timer_event(void *data, uint64_t expirations) |  | ||||||
| { |  | ||||||
| 	struct impl *impl = data; |  | ||||||
| 	struct session *sess; |  | ||||||
| 	uint64_t current_time = impl->next_time; |  | ||||||
| 
 |  | ||||||
| 	pw_log_debug("timeout"); |  | ||||||
| 	spa_list_for_each(sess, &impl->sessions, link) { |  | ||||||
| 		if (sess->state != SESSION_STATE_ESTABLISHED) |  | ||||||
| 			continue; |  | ||||||
| 		if (sess->next_time < current_time) |  | ||||||
| 			continue; |  | ||||||
| 
 |  | ||||||
| 		send_apple_midi_cmd_ck0(sess); |  | ||||||
| 	} |  | ||||||
| 	schedule_timeout(impl); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void copy_props(struct impl *impl, struct pw_properties *props, const char *key) | static void copy_props(struct impl *impl, struct pw_properties *props, const char *key) | ||||||
| { | { | ||||||
| 	const char *str; | 	const char *str; | ||||||
|  | @ -1681,7 +1648,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	struct pw_properties *props = NULL, *stream_props = NULL; | 	struct pw_properties *props = NULL, *stream_props = NULL; | ||||||
| 	uint16_t port; | 	uint16_t port; | ||||||
| 	const char *str; | 	const char *str; | ||||||
| 	struct timespec value, interval; |  | ||||||
| 	int res = 0; | 	int res = 0; | ||||||
| 
 | 
 | ||||||
| 	PW_LOG_TOPIC_INIT(mod_topic); | 	PW_LOG_TOPIC_INIT(mod_topic); | ||||||
|  | @ -1719,6 +1685,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	impl->context = context; | 	impl->context = context; | ||||||
| 	impl->loop = pw_context_get_main_loop(context); | 	impl->loop = pw_context_get_main_loop(context); | ||||||
| 	impl->data_loop = pw_context_acquire_loop(context, &props->dict); | 	impl->data_loop = pw_context_acquire_loop(context, &props->dict); | ||||||
|  | 	impl->timer_queue = pw_context_get_timer_queue(context); | ||||||
| 
 | 
 | ||||||
| 	pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name); | 	pw_properties_set(props, PW_KEY_NODE_LOOP_NAME, impl->data_loop->name); | ||||||
| 
 | 
 | ||||||
|  | @ -1820,18 +1787,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 			&impl->core_listener, | 			&impl->core_listener, | ||||||
| 			&core_events, impl); | 			&core_events, impl); | ||||||
| 
 | 
 | ||||||
| 	impl->timer = pw_loop_add_timer(impl->loop, on_timer_event, impl); |  | ||||||
| 	if (impl->timer == NULL) { |  | ||||||
| 		res = -errno; |  | ||||||
| 		pw_log_error("can't create timer source: %m"); |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
| 	value.tv_sec = 0; |  | ||||||
| 	value.tv_nsec = 1; |  | ||||||
| 	interval.tv_sec = 1; |  | ||||||
| 	interval.tv_nsec = 0; |  | ||||||
| 	pw_loop_update_timer(impl->loop, impl->timer, &value, &interval, false); |  | ||||||
| 
 |  | ||||||
| 	if ((res = setup_apple_session(impl)) < 0) | 	if ((res = setup_apple_session(impl)) < 0) | ||||||
| 		goto out; | 		goto out; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -188,6 +188,7 @@ struct impl { | ||||||
| 
 | 
 | ||||||
| 	struct pw_loop *main_loop; | 	struct pw_loop *main_loop; | ||||||
| 	struct pw_loop *data_loop; | 	struct pw_loop *data_loop; | ||||||
|  | 	struct pw_timer_queue *timer_queue; | ||||||
| 
 | 
 | ||||||
| 	struct pw_core *core; | 	struct pw_core *core; | ||||||
| 	struct spa_hook core_listener; | 	struct spa_hook core_listener; | ||||||
|  | @ -200,10 +201,10 @@ struct impl { | ||||||
| 	bool always_process; | 	bool always_process; | ||||||
| 	uint32_t cleanup_interval; | 	uint32_t cleanup_interval; | ||||||
| 
 | 
 | ||||||
| 	struct spa_source *standby_timer; | 	struct pw_timer standby_timer; | ||||||
| 	/* This timer is used when the first stream_start() call fails because
 | 	/* This timer is used when the first stream_start() call fails because
 | ||||||
| 	 * of an ENODEV error (see the stream_start() code for details) */ | 	 * of an ENODEV error (see the stream_start() code for details) */ | ||||||
| 	struct spa_source *stream_start_retry_timer; | 	struct pw_timer stream_start_retry_timer; | ||||||
| 
 | 
 | ||||||
| 	struct pw_properties *stream_props; | 	struct pw_properties *stream_props; | ||||||
| 	struct rtp_stream *stream; | 	struct rtp_stream *stream; | ||||||
|  | @ -216,7 +217,11 @@ struct impl { | ||||||
| 	uint8_t *buffer; | 	uint8_t *buffer; | ||||||
| 	size_t buffer_size; | 	size_t buffer_size; | ||||||
| 
 | 
 | ||||||
| 	bool receiving; | #define STATE_IDLE	0 | ||||||
|  | #define STATE_PROBE	1 | ||||||
|  | #define STATE_RECEIVING	2 | ||||||
|  | #define STATE_STOPPING	3 | ||||||
|  | 	int state; | ||||||
| 	bool may_pause; | 	bool may_pause; | ||||||
| 	bool standby; | 	bool standby; | ||||||
| 	bool waiting; | 	bool waiting; | ||||||
|  | @ -269,9 +274,11 @@ on_rtp_io(void *data, int fd, uint32_t mask) | ||||||
| 				goto receive_error; | 				goto receive_error; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (!impl->receiving) { | 		if (SPA_ATOMIC_LOAD(impl->state) != STATE_RECEIVING) { | ||||||
| 			impl->receiving = true; | 			if (!SPA_ATOMIC_CAS(impl->state, STATE_PROBE, STATE_RECEIVING)) { | ||||||
| 			pw_loop_invoke(impl->main_loop, do_start, 1, NULL, 0, false, impl); | 				if (SPA_ATOMIC_CAS(impl->state, STATE_IDLE, STATE_RECEIVING)) | ||||||
|  | 					pw_loop_invoke(impl->main_loop, do_start, 1, NULL, 0, false, impl); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return; | 	return; | ||||||
|  | @ -385,23 +392,6 @@ error: | ||||||
| 	return res; | 	return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void stream_open_connection(void *data, int *result); |  | ||||||
| 
 |  | ||||||
| static void on_open_connection_retry_timer_event(void *data, uint64_t expirations) |  | ||||||
| { |  | ||||||
| 	struct impl *impl = data; |  | ||||||
| 	pw_log_debug("trying again to open connection after previous attempt failed with ENODEV"); |  | ||||||
| 	stream_open_connection(impl, NULL); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void destroy_stream_start_retry_timer(struct impl *impl) |  | ||||||
| { |  | ||||||
| 	if (impl->stream_start_retry_timer != NULL) { |  | ||||||
| 		pw_loop_destroy_source(impl->main_loop, impl->stream_start_retry_timer); |  | ||||||
| 		impl->stream_start_retry_timer = NULL; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void stream_report_error(void *data, const char *error) | static void stream_report_error(void *data, const char *error) | ||||||
| { | { | ||||||
| 	struct impl *impl = data; | 	struct impl *impl = data; | ||||||
|  | @ -411,6 +401,15 @@ static void stream_report_error(void *data, const char *error) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void stream_open_connection(void *data, int *result); | ||||||
|  | 
 | ||||||
|  | static void on_open_connection_retry_timer_event(void *data) | ||||||
|  | { | ||||||
|  | 	struct impl *impl = data; | ||||||
|  | 	pw_log_debug("trying again to open connection after previous attempt failed with ENODEV"); | ||||||
|  | 	stream_open_connection(impl, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void stream_open_connection(void *data, int *result) | static void stream_open_connection(void *data, int *result) | ||||||
| { | { | ||||||
| 	int res = 0; | 	int res = 0; | ||||||
|  | @ -438,21 +437,12 @@ static void stream_open_connection(void *data, int *result) | ||||||
| 			pw_log_warn("failed to create socket because network device is not ready " | 			pw_log_warn("failed to create socket because network device is not ready " | ||||||
| 				"and present yet; will try again"); | 				"and present yet; will try again"); | ||||||
| 
 | 
 | ||||||
| 			if (impl->stream_start_retry_timer == NULL) { | 			pw_timer_queue_cancel(&impl->stream_start_retry_timer); | ||||||
| 				struct timespec value, interval; | 			/* Use a 1-second retry interval. The network interfaces
 | ||||||
| 
 | 			 * are likely to be up and running then. */ | ||||||
| 				impl->stream_start_retry_timer = pw_loop_add_timer(impl->main_loop, | 			pw_timer_queue_add(impl->timer_queue, &impl->stream_start_retry_timer, | ||||||
| 						on_open_connection_retry_timer_event, impl); | 					NULL, 1 * SPA_NSEC_PER_SEC, | ||||||
| 				/* Use a 1-second retry interval. The network interfaces
 | 					on_open_connection_retry_timer_event, impl); | ||||||
| 				 * are likely to be up and running then. */ |  | ||||||
| 				value.tv_sec = 1; |  | ||||||
| 				value.tv_nsec = 0; |  | ||||||
| 				interval.tv_sec = 1; |  | ||||||
| 				interval.tv_nsec = 0; |  | ||||||
| 				pw_loop_update_timer(impl->main_loop, impl->stream_start_retry_timer, &value, |  | ||||||
| 						&interval, false); |  | ||||||
| 			} |  | ||||||
| 			/* Do nothing if the timer is already up. */ |  | ||||||
| 
 | 
 | ||||||
| 			/* It is important to return 0 in this case. Otherwise, the nonzero return
 | 			/* It is important to return 0 in this case. Otherwise, the nonzero return
 | ||||||
| 			 * value will later be propagated through the core as an error. */ | 			 * value will later be propagated through the core as an error. */ | ||||||
|  | @ -463,7 +453,7 @@ static void stream_open_connection(void *data, int *result) | ||||||
| 			/* If ENODEV was returned earlier, and the stream_start_retry_timer
 | 			/* If ENODEV was returned earlier, and the stream_start_retry_timer
 | ||||||
| 			 * was consequently created, but then a non-ENODEV error occurred, | 			 * was consequently created, but then a non-ENODEV error occurred, | ||||||
| 			 * the timer must be stopped and removed. */ | 			 * the timer must be stopped and removed. */ | ||||||
| 			destroy_stream_start_retry_timer(impl); | 			pw_timer_queue_cancel(&impl->stream_start_retry_timer); | ||||||
| 			res = -errno; | 			res = -errno; | ||||||
| 			goto finish; | 			goto finish; | ||||||
| 		} | 		} | ||||||
|  | @ -471,7 +461,7 @@ static void stream_open_connection(void *data, int *result) | ||||||
| 
 | 
 | ||||||
| 	/* Cleanup the timer in case ENODEV occurred earlier, and this time,
 | 	/* Cleanup the timer in case ENODEV occurred earlier, and this time,
 | ||||||
| 	 * the socket creation succeeded. */ | 	 * the socket creation succeeded. */ | ||||||
| 	destroy_stream_start_retry_timer(impl); | 	pw_timer_queue_cancel(&impl->stream_start_retry_timer); | ||||||
| 
 | 
 | ||||||
| 	impl->source = pw_loop_add_io(impl->data_loop, fd, | 	impl->source = pw_loop_add_io(impl->data_loop, fd, | ||||||
| 				SPA_IO_IN, true, on_rtp_io, impl); | 				SPA_IO_IN, true, on_rtp_io, impl); | ||||||
|  | @ -504,7 +494,7 @@ static void stream_close_connection(void *data, int *result) | ||||||
| 
 | 
 | ||||||
| 	pw_log_info("stopping RTP listener"); | 	pw_log_info("stopping RTP listener"); | ||||||
| 
 | 
 | ||||||
| 	destroy_stream_start_retry_timer(impl); | 	pw_timer_queue_cancel(&impl->stream_start_retry_timer); | ||||||
| 
 | 
 | ||||||
| 	pw_loop_destroy_source(impl->data_loop, impl->source); | 	pw_loop_destroy_source(impl->data_loop, impl->source); | ||||||
| 	impl->source = NULL; | 	impl->source = NULL; | ||||||
|  | @ -586,14 +576,14 @@ static const struct rtp_stream_events stream_events = { | ||||||
| 	.param_changed = stream_param_changed, | 	.param_changed = stream_param_changed, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void on_standby_timer_event(void *data, uint64_t expirations) | static void on_standby_timer_event(void *data) | ||||||
| { | { | ||||||
| 	struct impl *impl = data; | 	struct impl *impl = data; | ||||||
| 
 | 
 | ||||||
| 	pw_log_debug("standby timer event; receiving: %d standby: %d waiting: %d", | 	pw_log_debug("standby timer event; state: %d standby: %d waiting: %d", | ||||||
| 			impl->receiving, impl->standby, impl->waiting); | 			impl->state, impl->standby, impl->waiting); | ||||||
| 
 | 
 | ||||||
| 	if (!impl->receiving) { | 	if (SPA_ATOMIC_CAS(impl->state, STATE_PROBE, STATE_STOPPING)) { | ||||||
| 		if (!impl->standby) { | 		if (!impl->standby) { | ||||||
| 			struct spa_dict_item item[1]; | 			struct spa_dict_item item[1]; | ||||||
| 
 | 
 | ||||||
|  | @ -608,10 +598,15 @@ static void on_standby_timer_event(void *data, uint64_t expirations) | ||||||
| 				rtp_stream_set_active(impl->stream, false); | 				rtp_stream_set_active(impl->stream, false); | ||||||
| 		} | 		} | ||||||
| 		//pw_impl_module_schedule_destroy(impl->module);
 | 		//pw_impl_module_schedule_destroy(impl->module);
 | ||||||
|  | 		SPA_ATOMIC_STORE(impl->state, STATE_IDLE); | ||||||
| 	} else { | 	} else { | ||||||
| 		pw_log_debug("timeout, keeping active RTP source"); | 		pw_log_debug("timeout, keeping active RTP source"); | ||||||
|  | 		SPA_ATOMIC_CAS(impl->state, STATE_RECEIVING, STATE_PROBE); | ||||||
| 	} | 	} | ||||||
| 	impl->receiving = false; | 
 | ||||||
|  | 	pw_timer_queue_add(impl->timer_queue, &impl->standby_timer, | ||||||
|  | 			&impl->standby_timer.timeout, impl->cleanup_interval * SPA_NSEC_PER_SEC, | ||||||
|  | 			on_standby_timer_event, impl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void core_destroy(void *d) | static void core_destroy(void *d) | ||||||
|  | @ -636,10 +631,8 @@ static void impl_destroy(struct impl *impl) | ||||||
| 	if (impl->core && impl->do_disconnect) | 	if (impl->core && impl->do_disconnect) | ||||||
| 		pw_core_disconnect(impl->core); | 		pw_core_disconnect(impl->core); | ||||||
| 
 | 
 | ||||||
| 	if (impl->standby_timer) | 	pw_timer_queue_cancel(&impl->standby_timer); | ||||||
| 		pw_loop_destroy_source(impl->main_loop, impl->standby_timer); | 	pw_timer_queue_cancel(&impl->stream_start_retry_timer); | ||||||
| 
 |  | ||||||
| 	destroy_stream_start_retry_timer(impl); |  | ||||||
| 
 | 
 | ||||||
| 	if (impl->data_loop) | 	if (impl->data_loop) | ||||||
| 		pw_context_release_loop(impl->context, impl->data_loop); | 		pw_context_release_loop(impl->context, impl->data_loop); | ||||||
|  | @ -695,7 +688,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	struct pw_context *context = pw_impl_module_get_context(module); | 	struct pw_context *context = pw_impl_module_get_context(module); | ||||||
| 	struct impl *impl; | 	struct impl *impl; | ||||||
| 	const char *str, *sess_name; | 	const char *str, *sess_name; | ||||||
| 	struct timespec value, interval; |  | ||||||
| 	struct pw_properties *props, *stream_props; | 	struct pw_properties *props, *stream_props; | ||||||
| 	int64_t ts_offset; | 	int64_t ts_offset; | ||||||
| 	char addr[128]; | 	char addr[128]; | ||||||
|  | @ -722,6 +714,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	impl->module = module; | 	impl->module = module; | ||||||
| 	impl->context = context; | 	impl->context = context; | ||||||
| 	impl->main_loop = pw_context_get_main_loop(context); | 	impl->main_loop = pw_context_get_main_loop(context); | ||||||
|  | 	impl->timer_queue = pw_context_get_timer_queue(context); | ||||||
| 	impl->data_loop = pw_context_acquire_loop(context, &props->dict); | 	impl->data_loop = pw_context_acquire_loop(context, &props->dict); | ||||||
| 
 | 
 | ||||||
| 	impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; | 	impl->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; | ||||||
|  | @ -830,17 +823,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 			&impl->core_listener, | 			&impl->core_listener, | ||||||
| 			&core_events, impl); | 			&core_events, impl); | ||||||
| 
 | 
 | ||||||
| 	impl->standby_timer = pw_loop_add_timer(impl->main_loop, on_standby_timer_event, impl); | 	if ((res = pw_timer_queue_add(impl->timer_queue, &impl->standby_timer, | ||||||
| 	if (impl->standby_timer == NULL) { | 			NULL, impl->cleanup_interval * SPA_NSEC_PER_SEC, | ||||||
| 		res = -errno; | 			on_standby_timer_event, impl)) < 0) { | ||||||
| 		pw_log_error("can't create timer source: %m"); | 		pw_log_error("can't add timer: %s", spa_strerror(res)); | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
| 	value.tv_sec = impl->cleanup_interval; |  | ||||||
| 	value.tv_nsec = 0; |  | ||||||
| 	interval.tv_sec = impl->cleanup_interval; |  | ||||||
| 	interval.tv_nsec = 0; |  | ||||||
| 	pw_loop_update_timer(impl->main_loop, impl->standby_timer, &value, &interval, false); |  | ||||||
| 
 | 
 | ||||||
| 	impl->stream = rtp_stream_new(impl->core, | 	impl->stream = rtp_stream_new(impl->core, | ||||||
| 			PW_DIRECTION_OUTPUT, pw_properties_copy(stream_props), | 			PW_DIRECTION_OUTPUT, pw_properties_copy(stream_props), | ||||||
|  |  | ||||||
|  | @ -168,6 +168,7 @@ struct impl { | ||||||
| 
 | 
 | ||||||
| 	struct pw_loop *main_loop; | 	struct pw_loop *main_loop; | ||||||
| 	struct pw_loop *data_loop; | 	struct pw_loop *data_loop; | ||||||
|  | 	struct pw_timer_queue *timer_queue; | ||||||
| 
 | 
 | ||||||
| 	struct pw_core *core; | 	struct pw_core *core; | ||||||
| 	struct spa_hook core_listener; | 	struct spa_hook core_listener; | ||||||
|  | @ -180,7 +181,7 @@ struct impl { | ||||||
| 
 | 
 | ||||||
| 	struct pw_properties *stream_props; | 	struct pw_properties *stream_props; | ||||||
| 
 | 
 | ||||||
| 	struct spa_source *timer; | 	struct pw_timer timer; | ||||||
| 
 | 
 | ||||||
| 	uint16_t src_port; | 	uint16_t src_port; | ||||||
| 	struct sockaddr_storage src_addr; | 	struct sockaddr_storage src_addr; | ||||||
|  | @ -562,7 +563,7 @@ static void destroy_stream(struct stream *s) | ||||||
| 	free(s); | 	free(s); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void on_timer_event(void *data, uint64_t expirations) | static void on_timer_event(void *data) | ||||||
| { | { | ||||||
| 	struct impl *impl = data; | 	struct impl *impl = data; | ||||||
| 	struct stream *s; | 	struct stream *s; | ||||||
|  | @ -576,6 +577,9 @@ static void on_timer_event(void *data, uint64_t expirations) | ||||||
| 		} | 		} | ||||||
| 		s->receiving = false; | 		s->receiving = false; | ||||||
| 	} | 	} | ||||||
|  | 	pw_timer_queue_add(impl->timer_queue, &impl->timer, | ||||||
|  | 			&impl->timer.timeout, impl->cleanup_interval * SPA_NSEC_PER_SEC, | ||||||
|  | 			on_timer_event, impl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void core_destroy(void *d) | static void core_destroy(void *d) | ||||||
|  | @ -602,8 +606,7 @@ static void impl_destroy(struct impl *impl) | ||||||
| 	if (impl->core && impl->do_disconnect) | 	if (impl->core && impl->do_disconnect) | ||||||
| 		pw_core_disconnect(impl->core); | 		pw_core_disconnect(impl->core); | ||||||
| 
 | 
 | ||||||
| 	if (impl->timer) | 	pw_timer_queue_cancel(&impl->timer); | ||||||
| 		pw_loop_destroy_source(impl->main_loop, impl->timer); |  | ||||||
| 
 | 
 | ||||||
| 	if (impl->data_loop) | 	if (impl->data_loop) | ||||||
| 		pw_context_release_loop(impl->context, impl->data_loop); | 		pw_context_release_loop(impl->context, impl->data_loop); | ||||||
|  | @ -658,7 +661,6 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	struct pw_context *context = pw_impl_module_get_context(module); | 	struct pw_context *context = pw_impl_module_get_context(module); | ||||||
| 	struct impl *impl; | 	struct impl *impl; | ||||||
| 	const char *str; | 	const char *str; | ||||||
| 	struct timespec value, interval; |  | ||||||
| 	struct pw_properties *props, *stream_props; | 	struct pw_properties *props, *stream_props; | ||||||
| 	int res = 0; | 	int res = 0; | ||||||
| 
 | 
 | ||||||
|  | @ -682,6 +684,7 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 	impl->module = module; | 	impl->module = module; | ||||||
| 	impl->context = context; | 	impl->context = context; | ||||||
| 	impl->main_loop = pw_context_get_main_loop(context); | 	impl->main_loop = pw_context_get_main_loop(context); | ||||||
|  | 	impl->timer_queue = pw_context_get_timer_queue(context); | ||||||
| 	impl->data_loop = pw_context_acquire_loop(context, &props->dict); | 	impl->data_loop = pw_context_acquire_loop(context, &props->dict); | ||||||
| 	spa_list_init(&impl->streams); | 	spa_list_init(&impl->streams); | ||||||
| 
 | 
 | ||||||
|  | @ -747,17 +750,12 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args) | ||||||
| 			&impl->core_listener, | 			&impl->core_listener, | ||||||
| 			&core_events, impl); | 			&core_events, impl); | ||||||
| 
 | 
 | ||||||
| 	impl->timer = pw_loop_add_timer(impl->main_loop, on_timer_event, impl); | 	if ((res = pw_timer_queue_add(impl->timer_queue, &impl->timer, | ||||||
| 	if (impl->timer == NULL) { | 			NULL, impl->cleanup_interval * SPA_NSEC_PER_SEC, | ||||||
| 		res = -errno; | 			on_timer_event, impl)) < 0) { | ||||||
| 		pw_log_error("can't create timer source: %m"); | 		pw_log_error("can't add timer: %s", spa_strerror(res)); | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
| 	value.tv_sec = impl->cleanup_interval; |  | ||||||
| 	value.tv_nsec = 0; |  | ||||||
| 	interval.tv_sec = impl->cleanup_interval; |  | ||||||
| 	interval.tv_nsec = 0; |  | ||||||
| 	pw_loop_update_timer(impl->main_loop, impl->timer, &value, &interval, false); |  | ||||||
| 
 | 
 | ||||||
| 	if ((res = listen_start(impl)) < 0) { | 	if ((res = listen_start(impl)) < 0) { | ||||||
| 		pw_log_error("failed to start VBAN stream: %s", spa_strerror(res)); | 		pw_log_error("failed to start VBAN stream: %s", spa_strerror(res)); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Wim Taymans
						Wim Taymans