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, ¶m, &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, ¶m, &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, ¶m, &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, ¶m, &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