1 /* SPA
2  *
3  * Copyright © 2019 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <spa/support/plugin.h>
26 #include <spa/support/log.h>
27 #include <spa/support/cpu.h>
28 
29 #include <spa/node/node.h>
30 #include <spa/node/io.h>
31 #include <spa/node/utils.h>
32 #include <spa/node/keys.h>
33 #include <spa/utils/names.h>
34 #include <spa/utils/result.h>
35 #include <spa/utils/string.h>
36 #include <spa/buffer/alloc.h>
37 #include <spa/pod/parser.h>
38 #include <spa/pod/filter.h>
39 #include <spa/param/param.h>
40 #include <spa/param/audio/format-utils.h>
41 #include <spa/param/latency-utils.h>
42 #include <spa/debug/format.h>
43 #include <spa/debug/pod.h>
44 
45 #undef SPA_LOG_TOPIC_DEFAULT
46 #define SPA_LOG_TOPIC_DEFAULT log_topic
47 static struct spa_log_topic *log_topic = &SPA_LOG_TOPIC(0, "spa.audioadapter");
48 
49 #define DEFAULT_ALIGN	16
50 
51 #define MAX_PORTS	SPA_AUDIO_MAX_CHANNELS
52 
53 /** \cond */
54 
55 struct impl {
56 	struct spa_handle handle;
57 	struct spa_node node;
58 
59 	struct spa_log *log;
60 	struct spa_cpu *cpu;
61 
62 	uint32_t max_align;
63 	enum spa_direction direction;
64 
65 	struct spa_node *target;
66 
67 	struct spa_node *follower;
68 	struct spa_hook follower_listener;
69 	uint32_t follower_flags;
70 	struct spa_audio_info follower_current_format;
71 
72 	struct spa_handle *hnd_convert;
73 	struct spa_node *convert;
74 	struct spa_hook convert_listener;
75 	uint32_t convert_flags;
76 
77 	uint32_t n_buffers;
78 	struct spa_buffer **buffers;
79 
80 	struct spa_io_buffers io_buffers;
81 	struct spa_io_rate_match io_rate_match;
82 	struct spa_io_position *io_position;
83 
84 	uint64_t info_all;
85 	struct spa_node_info info;
86 #define IDX_EnumFormat		0
87 #define IDX_PropInfo		1
88 #define IDX_Props		2
89 #define IDX_Format		3
90 #define IDX_EnumPortConfig	4
91 #define IDX_PortConfig		5
92 #define IDX_Latency		6
93 #define IDX_ProcessLatency	7
94 #define N_NODE_PARAMS		8
95 	struct spa_param_info params[N_NODE_PARAMS];
96 	uint32_t convert_params_flags[N_NODE_PARAMS];
97 	uint32_t follower_params_flags[N_NODE_PARAMS];
98 
99 	struct spa_hook_list hooks;
100 	struct spa_callbacks callbacks;
101 
102 	unsigned int add_listener:1;
103 	unsigned int have_format:1;
104 	unsigned int started:1;
105 	unsigned int driver:1;
106 	unsigned int async:1;
107 	unsigned int passthrough:1;
108 	unsigned int follower_removing:1;
109 };
110 
111 /** \endcond */
112 
follower_enum_params(struct impl * this,uint32_t id,uint32_t idx,struct spa_result_node_params * result,const struct spa_pod * filter,struct spa_pod_builder * builder)113 static int follower_enum_params(struct impl *this,
114 				 uint32_t id,
115 				 uint32_t idx,
116 				 struct spa_result_node_params *result,
117 				 const struct spa_pod *filter,
118 				 struct spa_pod_builder *builder)
119 {
120 	int res;
121 	if (result->next < 0x10000) {
122 		if ((res = spa_node_enum_params_sync(this->convert,
123 				id, &result->next, filter, &result->param, builder)) == 1)
124 			return res;
125 		result->next = 0x10000;
126 	}
127 	if (result->next >= 0x10000 && this->follower_params_flags[idx] & SPA_PARAM_INFO_READ) {
128 		result->next &= 0xffff;
129 		if ((res = spa_node_enum_params_sync(this->follower,
130 				id, &result->next, filter, &result->param, builder)) == 1) {
131 			result->next |= 0x10000;
132 			return res;
133 		}
134 	}
135 	return 0;
136 }
137 
impl_node_enum_params(void * object,int seq,uint32_t id,uint32_t start,uint32_t num,const struct spa_pod * filter)138 static int impl_node_enum_params(void *object, int seq,
139 				 uint32_t id, uint32_t start, uint32_t num,
140 				 const struct spa_pod *filter)
141 {
142 	struct impl *this = object;
143 	struct spa_pod_builder b = { 0 };
144 	uint8_t buffer[4096];
145 	struct spa_result_node_params result;
146 	uint32_t count = 0;
147 	int res;
148 
149 	spa_return_val_if_fail(this != NULL, -EINVAL);
150 	spa_return_val_if_fail(num != 0, -EINVAL);
151 
152 	result.id = id;
153 	result.next = start;
154 next:
155 	result.index = result.next;
156 
157 	spa_log_debug(this->log, "%p: %d id:%u", this, seq, id);
158 
159 	spa_pod_builder_init(&b, buffer, sizeof(buffer));
160 
161 	switch (id) {
162 	case SPA_PARAM_EnumPortConfig:
163 	case SPA_PARAM_PortConfig:
164 		res = spa_node_enum_params(this->convert, seq, id, start, num, filter);
165 		return res;
166 	case SPA_PARAM_PropInfo:
167 		if ((res = follower_enum_params(this,
168 				id, IDX_PropInfo, &result, filter, &b)) != 1)
169 			return res;
170 		break;
171 	case SPA_PARAM_Props:
172 		if ((res = follower_enum_params(this,
173 				id, IDX_Props, &result, filter, &b)) != 1)
174 			return res;
175 		break;
176 	case SPA_PARAM_ProcessLatency:
177 		if ((res = follower_enum_params(this,
178 				id, IDX_ProcessLatency, &result, filter, &b)) != 1)
179 			return res;
180 		break;
181 	case SPA_PARAM_EnumFormat:
182 	case SPA_PARAM_Format:
183 	case SPA_PARAM_Latency:
184 		if ((res = spa_node_port_enum_params_sync(this->follower,
185 				this->direction, 0,
186 				id, &result.next, filter, &result.param, &b)) != 1)
187 			return res;
188 		break;
189 	default:
190 		return -ENOENT;
191 	}
192 
193 	spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
194 
195 	if (++count != num)
196 		goto next;
197 
198 	return 0;
199 }
200 
link_io(struct impl * this)201 static int link_io(struct impl *this)
202 {
203 	int res;
204 
205 	if (this->convert == NULL)
206 		return 0;
207 
208 	spa_log_debug(this->log, "%p: controls", this);
209 
210 	spa_zero(this->io_rate_match);
211 	this->io_rate_match.rate = 1.0;
212 
213 	if ((res = spa_node_port_set_io(this->follower,
214 			this->direction, 0,
215 			SPA_IO_RateMatch,
216 			&this->io_rate_match, sizeof(this->io_rate_match))) < 0) {
217 		spa_log_debug(this->log, "%p: set RateMatch on follower disabled %d %s", this,
218 			res, spa_strerror(res));
219 	}
220 	else if ((res = spa_node_port_set_io(this->convert,
221 			SPA_DIRECTION_REVERSE(this->direction), 0,
222 			SPA_IO_RateMatch,
223 			&this->io_rate_match, sizeof(this->io_rate_match))) < 0) {
224 		spa_log_warn(this->log, "%p: set RateMatch on convert failed %d %s", this,
225 			res, spa_strerror(res));
226 	}
227 
228 	this->io_buffers = SPA_IO_BUFFERS_INIT;
229 
230 	if ((res = spa_node_port_set_io(this->follower,
231 			this->direction, 0,
232 			SPA_IO_Buffers,
233 			&this->io_buffers, sizeof(this->io_buffers))) < 0) {
234 		spa_log_warn(this->log, "%p: set Buffers on follower failed %d %s", this,
235 			res, spa_strerror(res));
236 		return res;
237 	}
238 	else if ((res = spa_node_port_set_io(this->convert,
239 			SPA_DIRECTION_REVERSE(this->direction), 0,
240 			SPA_IO_Buffers,
241 			&this->io_buffers, sizeof(this->io_buffers))) < 0) {
242 		spa_log_warn(this->log, "%p: set Buffers on convert failed %d %s", this,
243 			res, spa_strerror(res));
244 		return res;
245 	}
246 	return 0;
247 }
248 
emit_node_info(struct impl * this,bool full)249 static void emit_node_info(struct impl *this, bool full)
250 {
251 	uint32_t i;
252 	uint64_t old = full ? this->info.change_mask : 0;
253 
254 	if (full)
255 		this->info.change_mask = this->info_all;
256 	if (this->info.change_mask) {
257 		if (this->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) {
258 			for (i = 0; i < this->info.n_params; i++) {
259 				if (this->params[i].user > 0) {
260 					this->params[i].flags ^= SPA_PARAM_INFO_SERIAL;
261 					this->params[i].user = 0;
262 				}
263 			}
264 		}
265 		spa_node_emit_info(&this->hooks, &this->info);
266 		this->info.change_mask = old;
267 	}
268 }
269 
debug_params(struct impl * this,struct spa_node * node,enum spa_direction direction,uint32_t port_id,uint32_t id,struct spa_pod * filter,const char * debug,int err)270 static int debug_params(struct impl *this, struct spa_node *node,
271                 enum spa_direction direction, uint32_t port_id, uint32_t id, struct spa_pod *filter,
272 		const char *debug, int err)
273 {
274         struct spa_pod_builder b = { 0 };
275         uint8_t buffer[4096];
276         uint32_t state;
277         struct spa_pod *param;
278         int res;
279 
280         spa_log_error(this->log, "params %s: %d:%d (%s) %s",
281 			spa_debug_type_find_name(spa_type_param, id),
282 			direction, port_id, debug, spa_strerror(err));
283 	if (err == -EBUSY)
284 		return 0;
285 
286         state = 0;
287         while (true) {
288                 spa_pod_builder_init(&b, buffer, sizeof(buffer));
289                 res = spa_node_port_enum_params_sync(node,
290                                        direction, port_id,
291                                        id, &state,
292                                        NULL, &param, &b);
293                 if (res != 1) {
294 			if (res < 0)
295 				spa_log_error(this->log, "  error: %s", spa_strerror(res));
296                         break;
297 		}
298                 spa_debug_pod(2, NULL, param);
299         }
300 
301         spa_log_error(this->log, "failed filter:");
302         if (filter)
303                 spa_debug_pod(2, NULL, filter);
304 
305         return 0;
306 }
307 
negotiate_buffers(struct impl * this)308 static int negotiate_buffers(struct impl *this)
309 {
310 	uint8_t buffer[4096];
311 	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
312 	uint32_t state;
313 	struct spa_pod *param;
314 	int res;
315 	bool follower_alloc, conv_alloc;
316 	uint32_t i, size, buffers, blocks, align, flags, stride = 0;
317 	uint32_t *aligns;
318 	struct spa_data *datas;
319 	uint32_t follower_flags, conv_flags;
320 
321 	spa_log_debug(this->log, "%p: %d", this, this->n_buffers);
322 
323 	if (this->target == this->follower)
324 		return 0;
325 
326 	if (this->n_buffers > 0)
327 		return 0;
328 
329 	state = 0;
330 	param = NULL;
331 	if ((res = spa_node_port_enum_params_sync(this->follower,
332 				this->direction, 0,
333 				SPA_PARAM_Buffers, &state,
334 				param, &param, &b)) < 0) {
335 		if (res == -ENOENT)
336 			param = NULL;
337 		else {
338 			debug_params(this, this->follower, this->direction, 0,
339 				SPA_PARAM_Buffers, param, "follower buffers", res);
340 			return res;
341 		}
342 	}
343 
344 	state = 0;
345 	if ((res = spa_node_port_enum_params_sync(this->convert,
346 				SPA_DIRECTION_REVERSE(this->direction), 0,
347 				SPA_PARAM_Buffers, &state,
348 				param, &param, &b)) != 1) {
349 		debug_params(this, this->convert,
350 				SPA_DIRECTION_REVERSE(this->direction), 0,
351 				SPA_PARAM_Buffers, param, "convert buffers", res);
352 		return -ENOTSUP;
353 	}
354 	if (param == NULL)
355 		return -ENOTSUP;
356 
357 	spa_pod_fixate(param);
358 
359 	follower_flags = this->follower_flags;
360 	conv_flags = this->convert_flags;
361 
362 	follower_alloc = SPA_FLAG_IS_SET(follower_flags, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS);
363 	conv_alloc = SPA_FLAG_IS_SET(conv_flags, SPA_PORT_FLAG_CAN_ALLOC_BUFFERS);
364 
365 	flags = 0;
366 	if (conv_alloc || follower_alloc) {
367 		flags |= SPA_BUFFER_ALLOC_FLAG_NO_DATA;
368 		if (conv_alloc)
369 			follower_alloc = false;
370 	}
371 
372 	align = DEFAULT_ALIGN;
373 
374 	if ((res = spa_pod_parse_object(param,
375 			SPA_TYPE_OBJECT_ParamBuffers, NULL,
376 			SPA_PARAM_BUFFERS_buffers, SPA_POD_Int(&buffers),
377 			SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(&blocks),
378 			SPA_PARAM_BUFFERS_size,    SPA_POD_Int(&size),
379 			SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(&stride),
380 			SPA_PARAM_BUFFERS_align,   SPA_POD_OPT_Int(&align))) < 0)
381 		return res;
382 
383 	spa_log_debug(this->log, "%p: buffers:%d, blocks:%d, size:%d, stride:%d align:%d %d:%d",
384 			this, buffers, blocks, size, stride, align, follower_alloc, conv_alloc);
385 
386 	align = SPA_MAX(align, this->max_align);
387 
388 	datas = alloca(sizeof(struct spa_data) * blocks);
389 	memset(datas, 0, sizeof(struct spa_data) * blocks);
390 	aligns = alloca(sizeof(uint32_t) * blocks);
391 	for (i = 0; i < blocks; i++) {
392 		datas[i].type = SPA_DATA_MemPtr;
393 		datas[i].flags = SPA_DATA_FLAG_READWRITE | SPA_DATA_FLAG_DYNAMIC;
394 		datas[i].maxsize = size;
395 		aligns[i] = align;
396 	}
397 
398 	free(this->buffers);
399 	this->buffers = spa_buffer_alloc_array(buffers, flags, 0, NULL, blocks, datas, aligns);
400 	if (this->buffers == NULL)
401 		return -errno;
402 	this->n_buffers = buffers;
403 
404 	if ((res = spa_node_port_use_buffers(this->convert,
405 		       SPA_DIRECTION_REVERSE(this->direction), 0,
406 		       conv_alloc ? SPA_NODE_BUFFERS_FLAG_ALLOC : 0,
407 		       this->buffers, this->n_buffers)) < 0)
408 		return res;
409 
410 	if ((res = spa_node_port_use_buffers(this->follower,
411 		       this->direction, 0,
412 		       follower_alloc ? SPA_NODE_BUFFERS_FLAG_ALLOC : 0,
413 		       this->buffers, this->n_buffers)) < 0)
414 		return res;
415 
416 	return 0;
417 }
418 
configure_format(struct impl * this,uint32_t flags,const struct spa_pod * format)419 static int configure_format(struct impl *this, uint32_t flags, const struct spa_pod *format)
420 {
421 	int res;
422 
423 	spa_log_debug(this->log, "%p: configure format:", this);
424 	if (format && spa_log_level_enabled(this->log, SPA_LOG_LEVEL_DEBUG))
425 		spa_debug_format(0, NULL, format);
426 
427 	if (this->target != this->follower && this->convert) {
428 		if ((res = spa_node_port_set_param(this->convert,
429 					   SPA_DIRECTION_REVERSE(this->direction), 0,
430 					   SPA_PARAM_Format, flags,
431 					   format)) < 0)
432 				return res;
433 	}
434 
435 	if ((res = spa_node_port_set_param(this->follower,
436 					   this->direction, 0,
437 					   SPA_PARAM_Format, flags,
438 					   format)) < 0)
439 			return res;
440 
441 	this->have_format = format != NULL;
442 	if (format == NULL) {
443 		this->n_buffers = 0;
444 	} else {
445 		res = negotiate_buffers(this);
446 	}
447 
448 	return res;
449 }
450 
configure_convert(struct impl * this,uint32_t mode)451 static int configure_convert(struct impl *this, uint32_t mode)
452 {
453 	struct spa_pod_builder b = { 0 };
454 	uint8_t buffer[1024];
455 	struct spa_pod *param;
456 
457 	spa_pod_builder_init(&b, buffer, sizeof(buffer));
458 
459 	spa_log_debug(this->log, "%p: configure convert %p", this, this->target);
460 
461 	param = spa_pod_builder_add_object(&b,
462 		SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig,
463 		SPA_PARAM_PORT_CONFIG_direction,	SPA_POD_Id(this->direction),
464 		SPA_PARAM_PORT_CONFIG_mode,		SPA_POD_Id(mode));
465 
466 	return spa_node_set_param(this->convert, SPA_PARAM_PortConfig, 0, param);
467 }
468 
469 extern const struct spa_handle_factory spa_audioconvert_factory;
470 
471 static const struct spa_node_events follower_node_events;
472 
reconfigure_mode(struct impl * this,bool passthrough,enum spa_direction direction,struct spa_pod * format)473 static int reconfigure_mode(struct impl *this, bool passthrough,
474                 enum spa_direction direction, struct spa_pod *format)
475 {
476 	int res = 0;
477 	struct spa_hook l;
478 
479 	spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough);
480 
481 	if (this->passthrough != passthrough) {
482 		if (passthrough) {
483 			/* remove converter split/merge ports */
484 			configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_none);
485 		} else {
486 			/* remove follower ports */
487 			this->follower_removing = true;
488 			spa_zero(l);
489 			spa_node_add_listener(this->follower, &l, &follower_node_events, this);
490 			spa_hook_remove(&l);
491 			this->follower_removing = false;
492 		}
493 	}
494 
495 	/* set new target */
496 	this->target = passthrough ? this->follower : this->convert;
497 
498 	if ((res = configure_format(this, 0, format)) < 0)
499 		return res;
500 
501 	if (this->passthrough != passthrough) {
502 		this->passthrough = passthrough;
503 		if (passthrough) {
504 			/* add follower ports */
505 			spa_zero(l);
506 			spa_node_add_listener(this->follower, &l, &follower_node_events, this);
507 			spa_hook_remove(&l);
508 		} else {
509 			/* add converter ports */
510 			configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp);
511 			link_io(this);
512 		}
513 	}
514 
515 	this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS;
516 	this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE;
517 	this->params[IDX_Props].user++;
518 
519 	emit_node_info(this, false);
520 
521 	return 0;
522 }
523 
impl_node_set_param(void * object,uint32_t id,uint32_t flags,const struct spa_pod * param)524 static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
525 			       const struct spa_pod *param)
526 {
527 	int res = 0, res2 = 0;
528 	struct impl *this = object;
529 	struct spa_audio_info info = { 0 };
530 
531 	spa_log_debug(this->log, "%p: set param %d", this, id);
532 
533 	switch (id) {
534 	case SPA_PARAM_Format:
535 		if (this->started)
536 			return -EIO;
537 		if (param == NULL)
538 			return -EINVAL;
539 
540 		if ((res = spa_format_parse(param, &info.media_type, &info.media_subtype)) < 0)
541 			return res;
542 		if (info.media_type != SPA_MEDIA_TYPE_audio ||
543 			info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
544 				return -EINVAL;
545 		if (spa_format_audio_raw_parse(param, &info.info.raw) < 0)
546 			return -EINVAL;
547 
548 		this->follower_current_format = info;
549 		break;
550 
551 	case SPA_PARAM_PortConfig:
552 	{
553 		enum spa_direction dir;
554 		enum spa_param_port_config_mode mode;
555 		struct spa_pod *format = NULL;
556 		int monitor = false;
557 
558 		if (this->started) {
559 			spa_log_error(this->log, "was started");
560 			return -EIO;
561 		}
562 
563 		if (spa_pod_parse_object(param,
564 				SPA_TYPE_OBJECT_ParamPortConfig, NULL,
565 				SPA_PARAM_PORT_CONFIG_direction,	SPA_POD_Id(&dir),
566 				SPA_PARAM_PORT_CONFIG_mode,		SPA_POD_Id(&mode),
567 				SPA_PARAM_PORT_CONFIG_monitor,		SPA_POD_OPT_Bool(&monitor),
568 				SPA_PARAM_PORT_CONFIG_format,		SPA_POD_OPT_Pod(&format)) < 0)
569 			return -EINVAL;
570 
571 		switch (mode) {
572 		case SPA_PARAM_PORT_CONFIG_MODE_none:
573 			return -ENOTSUP;
574 		case SPA_PARAM_PORT_CONFIG_MODE_passthrough:
575 			if ((res = reconfigure_mode(this, true, dir, format)) < 0)
576 				return res;
577 			break;
578 		case SPA_PARAM_PORT_CONFIG_MODE_convert:
579 		case SPA_PARAM_PORT_CONFIG_MODE_dsp:
580 			if ((res = reconfigure_mode(this, false, dir, NULL)) < 0)
581 				return res;
582 			break;
583 		default:
584 			return -EINVAL;
585 		}
586 
587 		if (this->target != this->follower) {
588 			if ((res = spa_node_set_param(this->target, id, flags, param)) < 0)
589 				return res;
590 		}
591 		break;
592 	}
593 
594 	case SPA_PARAM_Props:
595 		if (this->target != this->follower)
596 			res = spa_node_set_param(this->target, id, flags, param);
597 		res2 = spa_node_set_param(this->follower, id, flags, param);
598 		if (res < 0 && res2 < 0)
599 			return res;
600 		res = 0;
601 		break;
602 	case SPA_PARAM_ProcessLatency:
603 		res = spa_node_set_param(this->follower, id, flags, param);
604 		break;
605 	default:
606 		res = -ENOTSUP;
607 		break;
608 	}
609 	return res;
610 }
611 
impl_node_set_io(void * object,uint32_t id,void * data,size_t size)612 static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
613 {
614 	struct impl *this = object;
615 	int res = 0;
616 
617 	spa_return_val_if_fail(this != NULL, -EINVAL);
618 
619 	switch (id) {
620 	case SPA_IO_Position:
621 		this->io_position = data;
622 		break;
623 	default:
624 		break;
625 	}
626 
627 	if (this->target)
628 		res = spa_node_set_io(this->target, id, data, size);
629 
630 	if (this->target != this->follower)
631 		res = spa_node_set_io(this->follower, id, data, size);
632 
633 	return res;
634 }
635 
negotiate_format(struct impl * this)636 static int negotiate_format(struct impl *this)
637 {
638 	uint32_t state;
639 	struct spa_pod *format;
640 	uint8_t buffer[4096];
641 	struct spa_pod_builder b = { 0 };
642 	int res;
643 
644 	if (this->have_format)
645 		return 0;
646 
647 	if (this->target == this->follower)
648 		return 0;
649 
650 	spa_pod_builder_init(&b, buffer, sizeof(buffer));
651 
652 	spa_log_debug(this->log, "%p: negiotiate", this);
653 
654 	spa_node_send_command(this->follower,
655 			&SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_ParamBegin));
656 
657 	state = 0;
658 	format = NULL;
659 	if ((res = spa_node_port_enum_params_sync(this->follower,
660 				this->direction, 0,
661 				SPA_PARAM_EnumFormat, &state,
662 				format, &format, &b)) < 0) {
663 		if (res == -ENOENT)
664 			format = NULL;
665 		else {
666 			debug_params(this, this->follower, this->direction, 0,
667 					SPA_PARAM_EnumFormat, format, "follower format", res);
668 			goto done;
669 		}
670 	}
671 
672 	if (this->convert) {
673 		state = 0;
674 		if ((res = spa_node_port_enum_params_sync(this->convert,
675 					SPA_DIRECTION_REVERSE(this->direction), 0,
676 					SPA_PARAM_EnumFormat, &state,
677 					format, &format, &b)) != 1) {
678 			debug_params(this, this->convert,
679 					SPA_DIRECTION_REVERSE(this->direction), 0,
680 					SPA_PARAM_EnumFormat, format, "convert format", res);
681 			res = -ENOTSUP;
682 			goto done;
683 		}
684 	}
685 	if (format == NULL) {
686 		res = -ENOTSUP;
687 		goto done;
688 	}
689 
690 	spa_pod_fixate(format);
691 
692 	res = configure_format(this, 0, format);
693 
694 done:
695 	spa_node_send_command(this->follower,
696 			&SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_ParamEnd));
697 
698 	return res;
699 }
700 
701 
impl_node_send_command(void * object,const struct spa_command * command)702 static int impl_node_send_command(void *object, const struct spa_command *command)
703 {
704 	struct impl *this = object;
705 	int res;
706 
707 	spa_return_val_if_fail(this != NULL, -EINVAL);
708 
709 	spa_log_debug(this->log, "%p: command %d", this, SPA_NODE_COMMAND_ID(command));
710 
711 	switch (SPA_NODE_COMMAND_ID(command)) {
712 	case SPA_NODE_COMMAND_Start:
713 		if ((res = negotiate_format(this)) < 0)
714 			return res;
715 		if ((res = negotiate_buffers(this)) < 0)
716 			return res;
717 		break;
718 	case SPA_NODE_COMMAND_Suspend:
719 		configure_format(this, 0, NULL);
720 		SPA_FALLTHROUGH
721 	case SPA_NODE_COMMAND_Flush:
722 		this->io_buffers.status = SPA_STATUS_OK;
723 		SPA_FALLTHROUGH
724 	case SPA_NODE_COMMAND_Pause:
725 		this->started = false;
726 		break;
727 	default:
728 		break;
729 	}
730 
731 	if ((res = spa_node_send_command(this->target, command)) < 0) {
732 		spa_log_error(this->log, "%p: can't send command %d: %s",
733 				this, SPA_NODE_COMMAND_ID(command),
734 				spa_strerror(res));
735 		return res;
736 	}
737 
738 	if (this->target != this->follower) {
739 		if ((res = spa_node_send_command(this->follower, command)) < 0) {
740 			spa_log_error(this->log, "%p: can't send command %d: %s",
741 					this, SPA_NODE_COMMAND_ID(command),
742 					spa_strerror(res));
743 			return res;
744 		}
745 	}
746 	switch (SPA_NODE_COMMAND_ID(command)) {
747 	case SPA_NODE_COMMAND_Start:
748 		this->started = true;
749 		break;
750 	}
751 	return res;
752 }
753 
convert_node_info(void * data,const struct spa_node_info * info)754 static void convert_node_info(void *data, const struct spa_node_info *info)
755 {
756 	struct impl *this = data;
757 	uint32_t i;
758 
759 	if (info->change_mask & SPA_NODE_CHANGE_MASK_PARAMS) {
760 		for (i = 0; i < info->n_params; i++) {
761 			uint32_t idx;
762 
763 			switch (info->params[i].id) {
764 			case SPA_PARAM_PropInfo:
765 				idx = IDX_PropInfo;
766 				break;
767 			case SPA_PARAM_Props:
768 				idx = IDX_Props;
769 				break;
770 			default:
771 				continue;
772 			}
773 			if (!this->add_listener &&
774 			    this->convert_params_flags[idx] == info->params[i].flags)
775 				continue;
776 
777 			this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
778 			this->convert_params_flags[idx] = info->params[i].flags;
779 			this->params[idx].flags =
780 				(this->params[idx].flags & SPA_PARAM_INFO_SERIAL) |
781 				(info->params[i].flags & SPA_PARAM_INFO_READWRITE);
782 
783 			if (!this->add_listener)
784 				this->params[idx].user++;
785 		}
786 	}
787 	emit_node_info(this, false);
788 }
789 
convert_port_info(void * data,enum spa_direction direction,uint32_t port_id,const struct spa_port_info * info)790 static void convert_port_info(void *data,
791 		enum spa_direction direction, uint32_t port_id,
792 		const struct spa_port_info *info)
793 {
794 	struct impl *this = data;
795 
796 	if (direction != this->direction) {
797 		if (port_id == 0)
798 			return;
799 		else
800 			port_id--;
801 	}
802 
803 	spa_log_trace(this->log, "%p: port info %d:%d", this,
804 			direction, port_id);
805 
806 	if (this->target != this->follower)
807 		spa_node_emit_port_info(&this->hooks, direction, port_id, info);
808 }
809 
convert_result(void * data,int seq,int res,uint32_t type,const void * result)810 static void convert_result(void *data, int seq, int res, uint32_t type, const void *result)
811 {
812 	struct impl *this = data;
813 
814 	if (this->target == this->follower)
815 		return;
816 
817 	spa_log_trace(this->log, "%p: result %d %d", this, seq, res);
818 	spa_node_emit_result(&this->hooks, seq, res, type, result);
819 }
820 
821 static const struct spa_node_events convert_node_events = {
822 	SPA_VERSION_NODE_EVENTS,
823 	.info = convert_node_info,
824 	.port_info = convert_port_info,
825 	.result = convert_result,
826 };
827 
follower_info(void * data,const struct spa_node_info * info)828 static void follower_info(void *data, const struct spa_node_info *info)
829 {
830 	struct impl *this = data;
831 	uint32_t i;
832 
833 	if (this->follower_removing)
834 		return;
835 
836 	this->async = (info->flags & SPA_NODE_FLAG_ASYNC) != 0;
837 
838 	if (info->max_input_ports > 0)
839 		this->direction = SPA_DIRECTION_INPUT;
840         else
841 		this->direction = SPA_DIRECTION_OUTPUT;
842 
843 	if (this->direction == SPA_DIRECTION_INPUT) {
844 		this->info.flags |= SPA_NODE_FLAG_IN_PORT_CONFIG;
845 		this->info.max_input_ports = MAX_PORTS;
846 	} else {
847 		this->info.flags |= SPA_NODE_FLAG_OUT_PORT_CONFIG;
848 		this->info.max_output_ports = MAX_PORTS;
849 	}
850 
851 	spa_log_debug(this->log, "%p: follower info %s", this,
852 			this->direction == SPA_DIRECTION_INPUT ?
853 				"Input" : "Output");
854 
855 	if (info->change_mask & SPA_NODE_CHANGE_MASK_PROPS) {
856 		this->info.change_mask |= SPA_NODE_CHANGE_MASK_PROPS;
857 		this->info.props = info->props;
858 	}
859 	if (info->change_mask & SPA_NODE_CHANGE_MASK_PARAMS) {
860 		for (i = 0; i < info->n_params; i++) {
861 			uint32_t idx;
862 
863 			switch (info->params[i].id) {
864 			case SPA_PARAM_PropInfo:
865 				idx = IDX_PropInfo;
866 				break;
867 			case SPA_PARAM_Props:
868 				idx = IDX_Props;
869 				break;
870 			case SPA_PARAM_ProcessLatency:
871 				idx = IDX_ProcessLatency;
872 				break;
873 			default:
874 				continue;
875 			}
876 			if (!this->add_listener &&
877 			    this->follower_params_flags[idx] == info->params[i].flags)
878 				continue;
879 
880 			this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
881 			this->follower_params_flags[idx] = info->params[i].flags;
882 			this->params[idx].flags =
883 				(this->params[idx].flags & SPA_PARAM_INFO_SERIAL) |
884 				(info->params[i].flags & SPA_PARAM_INFO_READWRITE);
885 
886 			if (!this->add_listener)
887 				this->params[idx].user++;
888 		}
889 	}
890 	emit_node_info(this, false);
891 }
892 
recalc_latency(struct impl * this,enum spa_direction direction,uint32_t port_id)893 static int recalc_latency(struct impl *this, enum spa_direction direction, uint32_t port_id)
894 {
895 	struct spa_pod_builder b = { 0 };
896 	uint8_t buffer[1024];
897 	struct spa_pod *param;
898 	uint32_t index = 0;
899 	struct spa_latency_info latency;
900 	int res;
901 
902 	spa_log_debug(this->log, "%p: ", this);
903 
904 	if (this->target == this->follower)
905 		return 0;
906 
907 	while (true) {
908 		spa_pod_builder_init(&b, buffer, sizeof(buffer));
909 		if ((res = spa_node_port_enum_params_sync(this->follower,
910 						direction, port_id, SPA_PARAM_Latency,
911 						&index, NULL, &param, &b)) != 1)
912 			return res;
913 		if ((res = spa_latency_parse(param, &latency)) < 0)
914 			return res;
915 		if (latency.direction == direction)
916 			break;
917 	}
918 	if ((res = spa_node_port_set_param(this->target,
919 					SPA_DIRECTION_REVERSE(direction), 0,
920 					SPA_PARAM_Latency, 0, param)) < 0)
921 		return res;
922 
923 	return 0;
924 }
925 
follower_port_info(void * data,enum spa_direction direction,uint32_t port_id,const struct spa_port_info * info)926 static void follower_port_info(void *data,
927 		enum spa_direction direction, uint32_t port_id,
928 		const struct spa_port_info *info)
929 {
930 	struct impl *this = data;
931 	uint32_t i;
932 	int res;
933 
934 	if (this->follower_removing) {
935 	      spa_node_emit_port_info(&this->hooks, direction, port_id, NULL);
936 	      return;
937 	}
938 
939 	spa_log_debug(this->log, "%p: follower port info %s %p %08"PRIx64, this,
940 			this->direction == SPA_DIRECTION_INPUT ?
941 				"Input" : "Output", info, info->change_mask);
942 
943 	if (info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) {
944 		for (i = 0; i < info->n_params; i++) {
945 			uint32_t idx;
946 
947 			switch (info->params[i].id) {
948 			case SPA_PARAM_EnumFormat:
949 				idx = IDX_EnumFormat;
950 				break;
951 			case SPA_PARAM_Format:
952 				idx = IDX_Format;
953 				break;
954 			case SPA_PARAM_Latency:
955 				idx = IDX_Latency;
956 				break;
957 			default:
958 				continue;
959 			}
960 			if (!this->add_listener &&
961 			    this->follower_params_flags[idx] == info->params[i].flags)
962 				continue;
963 
964 			this->follower_params_flags[idx] = info->params[i].flags;
965 			this->params[idx].flags =
966 				(this->params[idx].flags & SPA_PARAM_INFO_SERIAL) |
967 				(info->params[i].flags & SPA_PARAM_INFO_READWRITE);
968 
969 			if (idx == IDX_Latency) {
970 				res = recalc_latency(this, direction, port_id);
971 				spa_log_debug(this->log, "latency: %d", res);
972 			}
973 
974 			this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
975 			if (!this->add_listener)
976 				this->params[idx].user++;
977 		}
978 	}
979 	emit_node_info(this, false);
980 
981 	if (this->target == this->follower)
982 	      spa_node_emit_port_info(&this->hooks, direction, port_id, info);
983 }
984 
follower_result(void * data,int seq,int res,uint32_t type,const void * result)985 static void follower_result(void *data, int seq, int res, uint32_t type, const void *result)
986 {
987 	struct impl *this = data;
988 
989 	if (this->target != this->follower)
990 		return;
991 
992 	spa_log_trace(this->log, "%p: result %d %d", this, seq, res);
993 	spa_node_emit_result(&this->hooks, seq, res, type, result);
994 }
995 
996 static const struct spa_node_events follower_node_events = {
997 	SPA_VERSION_NODE_EVENTS,
998 	.info = follower_info,
999 	.port_info = follower_port_info,
1000 	.result = follower_result,
1001 };
1002 
follower_ready(void * data,int status)1003 static int follower_ready(void *data, int status)
1004 {
1005 	struct impl *this = data;
1006 
1007 	spa_log_trace_fp(this->log, "%p: ready %d", this, status);
1008 
1009 	if (this->target != this->follower) {
1010 		this->driver = true;
1011 
1012 		if (this->direction == SPA_DIRECTION_OUTPUT)
1013 			status = spa_node_process(this->convert);
1014 	}
1015 
1016 	return spa_node_call_ready(&this->callbacks, status);
1017 }
1018 
follower_reuse_buffer(void * data,uint32_t port_id,uint32_t buffer_id)1019 static int follower_reuse_buffer(void *data, uint32_t port_id, uint32_t buffer_id)
1020 {
1021 	int res;
1022 	struct impl *this = data;
1023 
1024 	if (this->target != this->follower && this->convert)
1025 		res = spa_node_port_reuse_buffer(this->convert, port_id, buffer_id);
1026 	else
1027 		res = spa_node_call_reuse_buffer(&this->callbacks, port_id, buffer_id);
1028 
1029 	return res;
1030 }
1031 
follower_xrun(void * data,uint64_t trigger,uint64_t delay,struct spa_pod * info)1032 static int follower_xrun(void *data, uint64_t trigger, uint64_t delay, struct spa_pod *info)
1033 {
1034 	struct impl *this = data;
1035 	return spa_node_call_xrun(&this->callbacks, trigger, delay, info);
1036 }
1037 
1038 static const struct spa_node_callbacks follower_node_callbacks = {
1039 	SPA_VERSION_NODE_CALLBACKS,
1040 	.ready = follower_ready,
1041 	.reuse_buffer = follower_reuse_buffer,
1042 	.xrun = follower_xrun,
1043 };
1044 
impl_node_add_listener(void * object,struct spa_hook * listener,const struct spa_node_events * events,void * data)1045 static int impl_node_add_listener(void *object,
1046 		struct spa_hook *listener,
1047 		const struct spa_node_events *events,
1048 		void *data)
1049 {
1050 	struct impl *this = object;
1051 	struct spa_hook l;
1052 	struct spa_hook_list save;
1053 
1054 	spa_return_val_if_fail(this != NULL, -EINVAL);
1055 
1056 	spa_log_trace(this->log, "%p: add listener %p", this, listener);
1057 	spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
1058 
1059 
1060 	if (events->info || events->port_info) {
1061 		this->add_listener = true;
1062 
1063 		spa_zero(l);
1064 		spa_node_add_listener(this->follower, &l, &follower_node_events, this);
1065 		spa_hook_remove(&l);
1066 
1067 		if (this->convert) {
1068 			spa_zero(l);
1069 			spa_node_add_listener(this->convert, &l, &convert_node_events, this);
1070 			spa_hook_remove(&l);
1071 		}
1072 		this->add_listener = false;
1073 
1074 		emit_node_info(this, true);
1075 	}
1076 	spa_hook_list_join(&this->hooks, &save);
1077 
1078 	return 0;
1079 }
1080 
1081 static int
impl_node_set_callbacks(void * object,const struct spa_node_callbacks * callbacks,void * data)1082 impl_node_set_callbacks(void *object,
1083 			const struct spa_node_callbacks *callbacks,
1084 			void *data)
1085 {
1086 	struct impl *this = object;
1087 
1088 	spa_return_val_if_fail(this != NULL, -EINVAL);
1089 
1090 	this->callbacks = SPA_CALLBACKS_INIT(callbacks, data);
1091 
1092 	return 0;
1093 }
1094 
1095 static int
impl_node_sync(void * object,int seq)1096 impl_node_sync(void *object, int seq)
1097 {
1098 	struct impl *this = object;
1099 
1100 	spa_return_val_if_fail(this != NULL, -EINVAL);
1101 
1102 	return spa_node_sync(this->follower, seq);
1103 }
1104 
1105 static int
impl_node_add_port(void * object,enum spa_direction direction,uint32_t port_id,const struct spa_dict * props)1106 impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id,
1107 		const struct spa_dict *props)
1108 {
1109 	struct impl *this = object;
1110 
1111 	spa_return_val_if_fail(this != NULL, -EINVAL);
1112 
1113 	if (direction != this->direction)
1114 		return -EINVAL;
1115 
1116 	return spa_node_add_port(this->target, direction, port_id, props);
1117 }
1118 
1119 static int
impl_node_remove_port(void * object,enum spa_direction direction,uint32_t port_id)1120 impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id)
1121 {
1122 	struct impl *this = object;
1123 
1124 	spa_return_val_if_fail(this != NULL, -EINVAL);
1125 
1126 	if (direction != this->direction)
1127 		return -EINVAL;
1128 
1129 	return spa_node_remove_port(this->target, direction, port_id);
1130 }
1131 
1132 static int
impl_node_port_enum_params(void * object,int seq,enum spa_direction direction,uint32_t port_id,uint32_t id,uint32_t start,uint32_t num,const struct spa_pod * filter)1133 impl_node_port_enum_params(void *object, int seq,
1134 			   enum spa_direction direction, uint32_t port_id,
1135 			   uint32_t id, uint32_t start, uint32_t num,
1136 			   const struct spa_pod *filter)
1137 {
1138 	struct impl *this = object;
1139 
1140 	spa_return_val_if_fail(this != NULL, -EINVAL);
1141 	spa_return_val_if_fail(num != 0, -EINVAL);
1142 
1143 	if (direction != this->direction)
1144 		port_id++;
1145 
1146 	spa_log_debug(this->log, "%p: %d %u", this, seq, id);
1147 
1148 	return spa_node_port_enum_params(this->target, seq, direction, port_id, id,
1149 			start, num, filter);
1150 }
1151 
1152 static int
impl_node_port_set_param(void * object,enum spa_direction direction,uint32_t port_id,uint32_t id,uint32_t flags,const struct spa_pod * param)1153 impl_node_port_set_param(void *object,
1154 			 enum spa_direction direction, uint32_t port_id,
1155 			 uint32_t id, uint32_t flags,
1156 			 const struct spa_pod *param)
1157 {
1158 	struct impl *this = object;
1159 	int res;
1160 
1161 	spa_return_val_if_fail(this != NULL, -EINVAL);
1162 
1163 	spa_log_debug(this->log, " %d %d %d %d", port_id, id, direction, this->direction);
1164 
1165 	if (direction != this->direction)
1166 		port_id++;
1167 
1168 	if ((res = spa_node_port_set_param(this->target, direction, port_id, id,
1169 			flags, param)) < 0)
1170 		return res;
1171 
1172 	if ((id == SPA_PARAM_Latency) &&
1173 	    direction == this->direction) {
1174 		if ((res = spa_node_port_set_param(this->follower, direction, 0, id,
1175 				flags, param)) < 0)
1176 			return res;
1177 	}
1178 
1179 	return res;
1180 }
1181 
1182 static int
impl_node_port_set_io(void * object,enum spa_direction direction,uint32_t port_id,uint32_t id,void * data,size_t size)1183 impl_node_port_set_io(void *object,
1184 		      enum spa_direction direction,
1185 		      uint32_t port_id,
1186 		      uint32_t id,
1187 		      void *data, size_t size)
1188 {
1189 	struct impl *this = object;
1190 
1191 	spa_return_val_if_fail(this != NULL, -EINVAL);
1192 
1193 	spa_log_debug(this->log, "set io %d %d %d %d", port_id, id, direction, this->direction);
1194 
1195 	if (direction != this->direction)
1196 		port_id++;
1197 
1198 	return spa_node_port_set_io(this->target, direction, port_id, id, data, size);
1199 }
1200 
1201 static int
impl_node_port_use_buffers(void * object,enum spa_direction direction,uint32_t port_id,uint32_t flags,struct spa_buffer ** buffers,uint32_t n_buffers)1202 impl_node_port_use_buffers(void *object,
1203 			   enum spa_direction direction,
1204 			   uint32_t port_id,
1205 			   uint32_t flags,
1206 			   struct spa_buffer **buffers,
1207 			   uint32_t n_buffers)
1208 {
1209 	struct impl *this = object;
1210 	int res;
1211 
1212 	spa_return_val_if_fail(this != NULL, -EINVAL);
1213 
1214 	if (direction != this->direction)
1215 		port_id++;
1216 
1217 	spa_log_debug(this->log, "%p: %d %d:%d", this,
1218 			n_buffers, direction, port_id);
1219 
1220 	if ((res = spa_node_port_use_buffers(this->target,
1221 					direction, port_id, flags, buffers, n_buffers)) < 0)
1222 		return res;
1223 
1224 	return res;
1225 }
1226 
1227 static int
impl_node_port_reuse_buffer(void * object,uint32_t port_id,uint32_t buffer_id)1228 impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
1229 {
1230 	struct impl *this = object;
1231 
1232 	spa_return_val_if_fail(this != NULL, -EINVAL);
1233 
1234 	return spa_node_port_reuse_buffer(this->target, port_id, buffer_id);
1235 }
1236 
impl_node_process(void * object)1237 static int impl_node_process(void *object)
1238 {
1239 	struct impl *this = object;
1240 	int status = 0, fstatus;
1241 
1242 	spa_log_trace_fp(this->log, "%p: process convert:%p driver:%d",
1243 			this, this->convert, this->driver);
1244 
1245 	if (this->target == this->follower) {
1246 		if (this->io_position)
1247 			this->io_rate_match.size = this->io_position->clock.duration;
1248 		return spa_node_process(this->follower);
1249 	}
1250 
1251 	if (this->direction == SPA_DIRECTION_INPUT) {
1252 		/* an input node (sink).
1253 		 * First we run the converter to process the input for the follower
1254 		 * then if it produced data, we run the follower. */
1255 		while (true) {
1256 			status = this->convert ? spa_node_process(this->convert) : 0;
1257 			/* schedule the follower when the converter needed
1258 			 * a recycled buffer */
1259 			if (status == -EPIPE || status == 0)
1260 				status = SPA_STATUS_HAVE_DATA;
1261 			else if (status < 0)
1262 				break;
1263 
1264 			if (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED)) {
1265 				/* as long as the converter produced something or
1266 				 * is drained, process the follower. */
1267 				fstatus = spa_node_process(this->follower);
1268 				if (fstatus < 0) {
1269 					status = fstatus;
1270 					break;
1271 				}
1272 				/* if the follower doesn't need more data or is
1273 				 * drained we can stop */
1274 				if ((fstatus & SPA_STATUS_NEED_DATA) == 0 ||
1275 				    (fstatus & SPA_STATUS_DRAINED))
1276 					break;
1277 			}
1278 			/* the converter needs more data */
1279 			if ((status & SPA_STATUS_NEED_DATA))
1280 				break;
1281 		}
1282 	} else if (!this->driver) {
1283 		bool done = false;
1284 		while (true) {
1285 			/* output node (source). First run the converter to make
1286 			 * sure we push out any queued data. Then when it needs
1287 			 * more data, schedule the follower. */
1288 			status = this->convert ? spa_node_process(this->convert) : 0;
1289 			if (status == 0)
1290 				status = SPA_STATUS_NEED_DATA;
1291 			else if (status < 0)
1292 				break;
1293 
1294 			done = (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED));
1295 
1296 			/* when not async, we can return the data when we are done.
1297 			 * In async mode we might first need to wake up the follower
1298 			 * to asynchronously provide more data for the next round. */
1299 			if (!this->async && done)
1300 				break;
1301 
1302 			if (status & SPA_STATUS_NEED_DATA) {
1303 				/* the converter needs more data, schedule the
1304 				 * follower */
1305 				fstatus = spa_node_process(this->follower);
1306 				if (fstatus < 0) {
1307 					status = fstatus;
1308 					break;
1309 				}
1310 				/* if the follower didn't produce more data or is
1311 				 * not drained we can stop now */
1312 				if ((fstatus & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED)) == 0)
1313 					break;
1314 			}
1315 			/* converter produced something or is drained and we
1316 			 * scheduled the follower above, we can stop now*/
1317 			if (done)
1318 				break;
1319 		}
1320 		if (!done)
1321 			spa_node_call_xrun(&this->callbacks, 0, 0, NULL);
1322 
1323 	} else {
1324 		status = spa_node_process(this->follower);
1325 	}
1326 	spa_log_trace_fp(this->log, "%p: process status:%d", this, status);
1327 
1328 	this->driver = false;
1329 
1330 	return status;
1331 }
1332 
1333 static const struct spa_node_methods impl_node = {
1334 	SPA_VERSION_NODE_METHODS,
1335 	.add_listener = impl_node_add_listener,
1336 	.set_callbacks = impl_node_set_callbacks,
1337 	.sync = impl_node_sync,
1338 	.enum_params = impl_node_enum_params,
1339 	.set_param = impl_node_set_param,
1340 	.set_io = impl_node_set_io,
1341 	.send_command = impl_node_send_command,
1342 	.add_port = impl_node_add_port,
1343 	.remove_port = impl_node_remove_port,
1344 	.port_enum_params = impl_node_port_enum_params,
1345 	.port_set_param = impl_node_port_set_param,
1346 	.port_use_buffers = impl_node_port_use_buffers,
1347 	.port_set_io = impl_node_port_set_io,
1348 	.port_reuse_buffer = impl_node_port_reuse_buffer,
1349 	.process = impl_node_process,
1350 };
1351 
impl_get_interface(struct spa_handle * handle,const char * type,void ** interface)1352 static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
1353 {
1354 	struct impl *this;
1355 
1356 	spa_return_val_if_fail(handle != NULL, -EINVAL);
1357 	spa_return_val_if_fail(interface != NULL, -EINVAL);
1358 
1359 	this = (struct impl *) handle;
1360 
1361 	if (spa_streq(type, SPA_TYPE_INTERFACE_Node))
1362 		*interface = &this->node;
1363 	else
1364 		return -ENOENT;
1365 
1366 	return 0;
1367 }
1368 
impl_clear(struct spa_handle * handle)1369 static int impl_clear(struct spa_handle *handle)
1370 {
1371 	struct impl *this;
1372 
1373 	spa_return_val_if_fail(handle != NULL, -EINVAL);
1374 
1375 	this = (struct impl *) handle;
1376 
1377 	spa_hook_remove(&this->follower_listener);
1378 	spa_node_set_callbacks(this->follower, NULL, NULL);
1379 
1380 	spa_handle_clear(this->hnd_convert);
1381 
1382 	if (this->buffers)
1383 		free(this->buffers);
1384 	this->buffers = NULL;
1385 
1386 	return 0;
1387 }
1388 
1389 
1390 static size_t
impl_get_size(const struct spa_handle_factory * factory,const struct spa_dict * params)1391 impl_get_size(const struct spa_handle_factory *factory,
1392 	      const struct spa_dict *params)
1393 {
1394 	size_t size;
1395 
1396 	size = spa_handle_factory_get_size(&spa_audioconvert_factory, params);
1397 	size += sizeof(struct impl);
1398 
1399 	return size;
1400 }
1401 
1402 static int
impl_init(const struct spa_handle_factory * factory,struct spa_handle * handle,const struct spa_dict * info,const struct spa_support * support,uint32_t n_support)1403 impl_init(const struct spa_handle_factory *factory,
1404 	  struct spa_handle *handle,
1405 	  const struct spa_dict *info,
1406 	  const struct spa_support *support,
1407 	  uint32_t n_support)
1408 {
1409 	struct impl *this;
1410 	void *iface;
1411 	const char *str;
1412 
1413 	spa_return_val_if_fail(factory != NULL, -EINVAL);
1414 	spa_return_val_if_fail(handle != NULL, -EINVAL);
1415 
1416 	handle->get_interface = impl_get_interface;
1417 	handle->clear = impl_clear;
1418 
1419 	this = (struct impl *) handle;
1420 
1421 	this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
1422 	spa_log_topic_init(this->log, log_topic);
1423 
1424 	this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU);
1425 
1426 	if (info == NULL ||
1427 	    (str = spa_dict_lookup(info, "audio.adapt.follower")) == NULL)
1428 		return -EINVAL;
1429 
1430 	sscanf(str, "pointer:%p", &this->follower);
1431 	if (this->follower == NULL)
1432 		return -EINVAL;
1433 
1434 	if (this->cpu)
1435 		this->max_align = spa_cpu_get_max_align(this->cpu);
1436 
1437 	spa_hook_list_init(&this->hooks);
1438 
1439 	this->node.iface = SPA_INTERFACE_INIT(
1440 			SPA_TYPE_INTERFACE_Node,
1441 			SPA_VERSION_NODE,
1442 			&impl_node, this);
1443 
1444 	this->hnd_convert = SPA_PTROFF(this, sizeof(struct impl), struct spa_handle);
1445 	spa_handle_factory_init(&spa_audioconvert_factory,
1446 				this->hnd_convert,
1447 				info, support, n_support);
1448 
1449 	spa_handle_get_interface(this->hnd_convert, SPA_TYPE_INTERFACE_Node, &iface);
1450 	this->convert = iface;
1451 	this->target = this->convert;
1452 
1453 	this->info_all = SPA_NODE_CHANGE_MASK_FLAGS |
1454 		SPA_NODE_CHANGE_MASK_PARAMS;
1455 	this->info = SPA_NODE_INFO_INIT();
1456 	this->info.flags = SPA_NODE_FLAG_RT |
1457 		SPA_NODE_FLAG_NEED_CONFIGURE;
1458 	this->params[IDX_EnumFormat] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
1459 	this->params[IDX_PropInfo] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
1460 	this->params[IDX_Props] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
1461 	this->params[IDX_Format] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
1462 	this->params[IDX_EnumPortConfig] = SPA_PARAM_INFO(SPA_PARAM_EnumPortConfig, SPA_PARAM_INFO_READ);
1463 	this->params[IDX_PortConfig] = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE);
1464 	this->params[IDX_Latency] = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE);
1465 	this->params[IDX_ProcessLatency] = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, SPA_PARAM_INFO_READWRITE);
1466 	this->info.params = this->params;
1467 	this->info.n_params = N_NODE_PARAMS;
1468 
1469 	spa_node_add_listener(this->follower,
1470 			&this->follower_listener, &follower_node_events, this);
1471 	spa_node_set_callbacks(this->follower, &follower_node_callbacks, this);
1472 
1473 	spa_node_add_listener(this->convert,
1474 			&this->convert_listener, &convert_node_events, this);
1475 
1476 	configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp);
1477 
1478 	link_io(this);
1479 
1480 	return 0;
1481 }
1482 
1483 static const struct spa_interface_info impl_interfaces[] = {
1484 	{ SPA_TYPE_INTERFACE_Node, },
1485 };
1486 
1487 static int
impl_enum_interface_info(const struct spa_handle_factory * factory,const struct spa_interface_info ** info,uint32_t * index)1488 impl_enum_interface_info(const struct spa_handle_factory *factory,
1489 			 const struct spa_interface_info **info,
1490 			 uint32_t *index)
1491 {
1492 	spa_return_val_if_fail(factory != NULL, -EINVAL);
1493 	spa_return_val_if_fail(info != NULL, -EINVAL);
1494 	spa_return_val_if_fail(index != NULL, -EINVAL);
1495 
1496 	switch (*index) {
1497 	case 0:
1498 		*info = &impl_interfaces[*index];
1499 		break;
1500 	default:
1501 		return 0;
1502 	}
1503 	(*index)++;
1504 	return 1;
1505 }
1506 
1507 const struct spa_handle_factory spa_audioadapter_factory = {
1508 	.version = SPA_VERSION_HANDLE_FACTORY,
1509 	.name = SPA_NAME_AUDIO_ADAPT,
1510 	.get_size = impl_get_size,
1511 	.init = impl_init,
1512 	.enum_interface_info = impl_enum_interface_info,
1513 };
1514