Add default-monitor-time-sec
[platform/upstream/pulseaudio.git] / src / pulsecore / core.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as published
9   by the Free Software Foundation; either version 2.1 of the License,
10   or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <signal.h>
28
29 #include <pulse/rtclock.h>
30 #include <pulse/timeval.h>
31 #include <pulse/xmalloc.h>
32
33 #include <pulsecore/module.h>
34 #include <pulsecore/core-rtclock.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/message-handler.h>
37 #include <pulsecore/core-scache.h>
38 #include <pulsecore/core-subscribe.h>
39 #include <pulsecore/random.h>
40 #include <pulsecore/log.h>
41 #include <pulsecore/macro.h>
42 #include <pulsecore/strbuf.h>
43
44 #include "core.h"
45
46 PA_DEFINE_PUBLIC_CLASS(pa_core, pa_msgobject);
47
48 static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
49     pa_core *c = PA_CORE(o);
50
51     pa_core_assert_ref(c);
52
53     switch (code) {
54
55         case PA_CORE_MESSAGE_UNLOAD_MODULE:
56             pa_module_unload(userdata, true);
57             return 0;
58
59         default:
60             return -1;
61     }
62 }
63
64 static void core_free(pa_object *o);
65
66 /* Returns a list of handlers. */
67 static char *message_handler_list(pa_core *c) {
68     pa_json_encoder *encoder;
69     void *state = NULL;
70     struct pa_message_handler *handler;
71
72     encoder = pa_json_encoder_new();
73
74     pa_json_encoder_begin_element_array(encoder);
75     PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
76         pa_json_encoder_begin_element_object(encoder);
77
78         pa_json_encoder_add_member_string(encoder, "name", handler->object_path);
79         pa_json_encoder_add_member_string(encoder, "description", handler->description);
80
81         pa_json_encoder_end_object(encoder);
82     }
83     pa_json_encoder_end_array(encoder);
84
85     return pa_json_encoder_to_string_free(encoder);
86 }
87
88 static int core_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
89     pa_core *c;
90
91     pa_assert(c = (pa_core *) userdata);
92     pa_assert(message);
93     pa_assert(response);
94     pa_assert(pa_safe_streq(object_path, "/core"));
95
96     if (pa_streq(message, "list-handlers")) {
97         *response = message_handler_list(c);
98         return PA_OK;
99     }
100
101     return -PA_ERR_NOTIMPLEMENTED;
102 }
103
104 pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size) {
105     pa_core* c;
106     pa_mempool *pool;
107     pa_mem_type_t type;
108     int j;
109
110     pa_assert(m);
111
112     if (shared) {
113         type = (enable_memfd) ? PA_MEM_TYPE_SHARED_MEMFD : PA_MEM_TYPE_SHARED_POSIX;
114         if (!(pool = pa_mempool_new(type, shm_size, false))) {
115             pa_log_warn("Failed to allocate %s memory pool. Falling back to a normal memory pool.",
116                         pa_mem_type_to_string(type));
117             shared = false;
118         }
119     }
120
121     if (!shared) {
122         if (!(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, shm_size, false))) {
123             pa_log("pa_mempool_new() failed.");
124             return NULL;
125         }
126     }
127
128     c = pa_msgobject_new(pa_core);
129     c->parent.parent.free = core_free;
130     c->parent.process_msg = core_process_msg;
131
132     c->state = PA_CORE_STARTUP;
133     c->mainloop = m;
134
135     c->clients = pa_idxset_new(NULL, NULL);
136     c->cards = pa_idxset_new(NULL, NULL);
137     c->sinks = pa_idxset_new(NULL, NULL);
138     c->sources = pa_idxset_new(NULL, NULL);
139     c->sink_inputs = pa_idxset_new(NULL, NULL);
140     c->source_outputs = pa_idxset_new(NULL, NULL);
141     c->modules = pa_idxset_new(NULL, NULL);
142     c->scache = pa_idxset_new(NULL, NULL);
143
144     c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
145     c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
146     c->message_handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
147
148     pa_message_handler_register(c, "/core", "Core message handler", core_message_handler, (void *) c);
149
150     c->default_source = NULL;
151     c->default_sink = NULL;
152
153     c->default_sample_spec.format = PA_SAMPLE_S16NE;
154     c->default_sample_spec.rate = 44100;
155     c->default_sample_spec.channels = 2;
156     pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
157     c->default_n_fragments = 4;
158     c->default_fragment_size_msec = 25;
159
160     c->deferred_volume_safety_margin_usec = 8000;
161     c->deferred_volume_extra_delay_usec = 0;
162
163     c->module_defer_unload_event = NULL;
164     c->modules_pending_unload = pa_hashmap_new(NULL, NULL);
165
166     c->subscription_defer_event = NULL;
167     PA_LLIST_HEAD_INIT(pa_subscription, c->subscriptions);
168     PA_LLIST_HEAD_INIT(pa_subscription_event, c->subscription_event_queue);
169     c->subscription_event_last = NULL;
170
171     c->mempool = pool;
172     c->shm_size = shm_size;
173     pa_silence_cache_init(&c->silence_cache);
174
175     c->exit_event = NULL;
176     c->scache_auto_unload_event = NULL;
177
178     c->exit_idle_time = -1;
179     c->scache_idle_time = 20;
180
181     c->flat_volumes = true;
182     c->disallow_module_loading = false;
183     c->disallow_exit = false;
184     c->running_as_daemon = false;
185     c->realtime_scheduling = false;
186     c->realtime_priority = 5;
187     c->disable_remixing = false;
188     c->remixing_use_all_sink_channels = true;
189     c->remixing_produce_lfe = false;
190     c->remixing_consume_lfe = false;
191     c->lfe_crossover_freq = 0;
192     c->deferred_volume = true;
193     c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
194
195     for (j = 0; j < PA_CORE_HOOK_MAX; j++)
196         pa_hook_init(&c->hooks[j], c);
197
198     pa_random(&c->cookie, sizeof(c->cookie));
199
200 #ifdef SIGPIPE
201     pa_check_signal_is_blocked(SIGPIPE);
202 #endif
203
204 #ifdef TIZEN_PCM_DUMP
205     c->pcm_dump = 0;
206 #endif
207 #ifdef TIZEN_EMPTY_POP
208     c->empty_pop_threshold = 60;  /* will be updated by pa_daemon_conf_load() */
209 #endif
210
211     return c;
212 }
213
214 static void core_free(pa_object *o) {
215     pa_core *c = PA_CORE(o);
216     int j;
217     pa_assert(c);
218
219     c->state = PA_CORE_SHUTDOWN;
220
221     /* Note: All modules and samples in the cache should be unloaded before
222      * we get here */
223
224     pa_assert(pa_idxset_isempty(c->scache));
225     pa_idxset_free(c->scache, NULL);
226
227     pa_assert(pa_idxset_isempty(c->modules));
228     pa_idxset_free(c->modules, NULL);
229
230     pa_assert(pa_idxset_isempty(c->clients));
231     pa_idxset_free(c->clients, NULL);
232
233     pa_assert(pa_idxset_isempty(c->cards));
234     pa_idxset_free(c->cards, NULL);
235
236     pa_assert(pa_idxset_isempty(c->sinks));
237     pa_idxset_free(c->sinks, NULL);
238
239     pa_assert(pa_idxset_isempty(c->sources));
240     pa_idxset_free(c->sources, NULL);
241
242     pa_assert(pa_idxset_isempty(c->source_outputs));
243     pa_idxset_free(c->source_outputs, NULL);
244
245     pa_assert(pa_idxset_isempty(c->sink_inputs));
246     pa_idxset_free(c->sink_inputs, NULL);
247
248     pa_assert(pa_hashmap_isempty(c->namereg));
249     pa_hashmap_free(c->namereg);
250
251     pa_assert(pa_hashmap_isempty(c->shared));
252     pa_hashmap_free(c->shared);
253
254     pa_message_handler_unregister(c, "/core");
255
256     pa_assert(pa_hashmap_isempty(c->message_handlers));
257     pa_hashmap_free(c->message_handlers);
258
259     pa_assert(pa_hashmap_isempty(c->modules_pending_unload));
260     pa_hashmap_free(c->modules_pending_unload);
261
262     pa_subscription_free_all(c);
263
264     if (c->exit_event)
265         c->mainloop->time_free(c->exit_event);
266
267     pa_assert(!c->default_source);
268     pa_assert(!c->default_sink);
269     pa_xfree(c->configured_default_source);
270     pa_xfree(c->configured_default_sink);
271
272     pa_silence_cache_done(&c->silence_cache);
273     pa_mempool_unref(c->mempool);
274
275     for (j = 0; j < PA_CORE_HOOK_MAX; j++)
276         pa_hook_done(&c->hooks[j]);
277
278     pa_xfree(c);
279 }
280
281 void pa_core_set_configured_default_sink(pa_core *core, const char *sink) {
282     char *old_sink;
283
284     pa_assert(core);
285
286     old_sink = pa_xstrdup(core->configured_default_sink);
287
288     if (pa_safe_streq(sink, old_sink))
289         goto finish;
290
291     pa_xfree(core->configured_default_sink);
292     core->configured_default_sink = pa_xstrdup(sink);
293     pa_log_info("configured_default_sink: %s -> %s",
294                 old_sink ? old_sink : "(unset)", sink ? sink : "(unset)");
295     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
296
297     pa_core_update_default_sink(core);
298
299 finish:
300     pa_xfree(old_sink);
301 }
302
303 void pa_core_set_configured_default_source(pa_core *core, const char *source) {
304     char *old_source;
305
306     pa_assert(core);
307
308     old_source = pa_xstrdup(core->configured_default_source);
309
310     if (pa_safe_streq(source, old_source))
311         goto finish;
312
313     pa_xfree(core->configured_default_source);
314     core->configured_default_source = pa_xstrdup(source);
315     pa_log_info("configured_default_source: %s -> %s",
316                 old_source ? old_source : "(unset)", source ? source : "(unset)");
317     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
318
319     pa_core_update_default_source(core);
320
321 finish:
322     pa_xfree(old_source);
323 }
324
325 /* a  < b  ->  return -1
326  * a == b  ->  return  0
327  * a  > b  ->  return  1 */
328 static int compare_sinks(pa_sink *a, pa_sink *b) {
329     pa_core *core;
330
331     core = a->core;
332
333     /* Available sinks always beat unavailable sinks. */
334     if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
335             && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
336         return -1;
337     if (b->active_port && b->active_port->available == PA_AVAILABLE_NO
338             && (!a->active_port || a->active_port->available != PA_AVAILABLE_NO))
339         return 1;
340
341     /* The configured default sink is preferred over any other sink. */
342     if (pa_safe_streq(b->name, core->configured_default_sink))
343         return -1;
344     if (pa_safe_streq(a->name, core->configured_default_sink))
345         return 1;
346
347     if (a->priority < b->priority)
348         return -1;
349     if (a->priority > b->priority)
350         return 1;
351
352     /* It's hard to find any difference between these sinks, but maybe one of
353      * them is already the default sink? If so, it's best to keep it as the
354      * default to avoid changing the routing for no good reason. */
355     if (b == core->default_sink)
356         return -1;
357     if (a == core->default_sink)
358         return 1;
359
360     return 0;
361 }
362
363 void pa_core_update_default_sink(pa_core *core) {
364     pa_sink *best = NULL;
365     pa_sink *sink;
366     uint32_t idx;
367     pa_sink *old_default_sink;
368
369     pa_assert(core);
370
371     PA_IDXSET_FOREACH(sink, core->sinks, idx) {
372         if (!PA_SINK_IS_LINKED(sink->state))
373             continue;
374
375         if (!best) {
376             best = sink;
377             continue;
378         }
379
380         if (compare_sinks(sink, best) > 0)
381             best = sink;
382     }
383
384     old_default_sink = core->default_sink;
385
386     if (best == old_default_sink)
387         return;
388
389     core->default_sink = best;
390     pa_log_info("default_sink: %s -> %s",
391                 old_default_sink ? old_default_sink->name : "(unset)", best ? best->name : "(unset)");
392
393     /* If the default sink changed, it may be that the default source has to be
394      * changed too, because monitor sources are prioritized partly based on the
395      * priorities of the monitored sinks. */
396     pa_core_update_default_source(core);
397
398     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
399     pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink);
400
401     /* try to move the streams from old_default_sink to the new default_sink conditionally */
402     if (old_default_sink)
403         pa_sink_move_streams_to_default_sink(core, old_default_sink, true);
404 }
405
406 /* a  < b  ->  return -1
407  * a == b  ->  return  0
408  * a  > b  ->  return  1 */
409 static int compare_sources(pa_source *a, pa_source *b) {
410     pa_core *core;
411
412     core = a->core;
413
414     /* Available sources always beat unavailable sources. */
415     if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
416             && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
417         return -1;
418     if (b->active_port && b->active_port->available == PA_AVAILABLE_NO
419             && (!a->active_port || a->active_port->available != PA_AVAILABLE_NO))
420         return 1;
421
422     /* The configured default source is preferred over any other source. */
423     if (pa_safe_streq(b->name, core->configured_default_source))
424         return -1;
425     if (pa_safe_streq(a->name, core->configured_default_source))
426         return 1;
427
428     /* Monitor sources lose to non-monitor sources. */
429     if (a->monitor_of && !b->monitor_of)
430         return -1;
431     if (!a->monitor_of && b->monitor_of)
432         return 1;
433
434     if (a->priority < b->priority)
435         return -1;
436     if (a->priority > b->priority)
437         return 1;
438
439     /* If the sources are monitors, we can compare the monitored sinks. */
440     if (a->monitor_of)
441         return compare_sinks(a->monitor_of, b->monitor_of);
442
443     /* It's hard to find any difference between these sources, but maybe one of
444      * them is already the default source? If so, it's best to keep it as the
445      * default to avoid changing the routing for no good reason. */
446     if (b == core->default_source)
447         return -1;
448     if (a == core->default_source)
449         return 1;
450
451     return 0;
452 }
453
454 void pa_core_update_default_source(pa_core *core) {
455     pa_source *best = NULL;
456     pa_source *source;
457     uint32_t idx;
458     pa_source *old_default_source;
459
460     pa_assert(core);
461
462     PA_IDXSET_FOREACH(source, core->sources, idx) {
463         if (!PA_SOURCE_IS_LINKED(source->state))
464             continue;
465
466         if (!best) {
467             best = source;
468             continue;
469         }
470
471         if (compare_sources(source, best) > 0)
472             best = source;
473     }
474
475     old_default_source = core->default_source;
476
477     if (best == old_default_source)
478         return;
479
480     core->default_source = best;
481     pa_log_info("default_source: %s -> %s",
482                 old_default_source ? old_default_source->name : "(unset)", best ? best->name : "(unset)");
483     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
484     pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], core->default_source);
485
486     /* try to move the streams from old_default_source to the new default_source conditionally */
487     if (old_default_source)
488         pa_source_move_streams_to_default_source(core, old_default_source, true);
489 }
490
491 void pa_core_set_exit_idle_time(pa_core *core, int time) {
492     pa_assert(core);
493
494     if (time == core->exit_idle_time)
495         return;
496
497     pa_log_info("exit_idle_time: %i -> %i", core->exit_idle_time, time);
498     core->exit_idle_time = time;
499 }
500
501 static void exit_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
502     pa_core *c = userdata;
503     pa_assert(c->exit_event == e);
504
505     pa_log_info("We are idle, quitting...");
506     pa_core_exit(c, true, 0);
507 }
508
509 void pa_core_check_idle(pa_core *c) {
510     pa_assert(c);
511
512     if (!c->exit_event &&
513         c->exit_idle_time >= 0 &&
514         pa_idxset_size(c->clients) == 0) {
515
516         c->exit_event = pa_core_rttime_new(c, pa_rtclock_now() + c->exit_idle_time * PA_USEC_PER_SEC, exit_callback, c);
517
518     } else if (c->exit_event && pa_idxset_size(c->clients) > 0) {
519         c->mainloop->time_free(c->exit_event);
520         c->exit_event = NULL;
521     }
522 }
523
524 int pa_core_exit(pa_core *c, bool force, int retval) {
525     pa_assert(c);
526
527     if (c->disallow_exit && !force)
528         return -1;
529
530     c->mainloop->quit(c->mainloop, retval);
531     return 0;
532 }
533
534 void pa_core_maybe_vacuum(pa_core *c) {
535     pa_assert(c);
536
537     if (pa_idxset_isempty(c->sink_inputs) && pa_idxset_isempty(c->source_outputs)) {
538         pa_log_debug("Hmm, no streams around, trying to vacuum.");
539     } else {
540         pa_sink *si;
541         pa_source *so;
542         uint32_t idx;
543
544         idx = 0;
545         PA_IDXSET_FOREACH(si, c->sinks, idx)
546             if (si->state != PA_SINK_SUSPENDED)
547                 return;
548
549         idx = 0;
550         PA_IDXSET_FOREACH(so, c->sources, idx)
551             if (so->state != PA_SOURCE_SUSPENDED)
552                 return;
553
554         pa_log_info("All sinks and sources are suspended, vacuuming memory");
555     }
556
557     pa_mempool_vacuum(c->mempool);
558 }
559
560 pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) {
561     struct timeval tv;
562
563     pa_assert(c);
564     pa_assert(c->mainloop);
565
566     return c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, usec, true), cb, userdata);
567 }
568
569 void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec) {
570     struct timeval tv;
571
572     pa_assert(c);
573     pa_assert(c->mainloop);
574
575     c->mainloop->time_restart(e, pa_timeval_rtstore(&tv, usec, true));
576 }
577
578 void pa_core_move_streams_to_newly_available_preferred_sink(pa_core *c, pa_sink *s) {
579     pa_sink_input *si;
580     uint32_t idx;
581
582     pa_assert(c);
583     pa_assert(s);
584
585     PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
586         if (si->sink == s)
587             continue;
588
589         if (!si->sink)
590             continue;
591
592         /* Skip this sink input if it is connecting a filter sink to
593          * the master */
594         if (si->origin_sink)
595             continue;
596
597         /* It might happen that a stream and a sink are set up at the
598            same time, in which case we want to make sure we don't
599            interfere with that */
600         if (!PA_SINK_INPUT_IS_LINKED(si->state))
601             continue;
602
603         if (pa_safe_streq(si->preferred_sink, s->name))
604             pa_sink_input_move_to(si, s, false);
605     }
606
607 }
608
609 void pa_core_move_streams_to_newly_available_preferred_source(pa_core *c, pa_source *s) {
610     pa_source_output *so;
611     uint32_t idx;
612
613     pa_assert(c);
614     pa_assert(s);
615
616     PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
617         if (so->source == s)
618             continue;
619
620         if (so->direct_on_input)
621             continue;
622
623         if (!so->source)
624             continue;
625
626         /* Skip this source output if it is connecting a filter source to
627          * the master */
628         if (so->destination_source)
629             continue;
630
631         /* It might happen that a stream and a source are set up at the
632            same time, in which case we want to make sure we don't
633            interfere with that */
634         if (!PA_SOURCE_OUTPUT_IS_LINKED(so->state))
635             continue;
636
637         if (pa_safe_streq(so->preferred_source, s->name))
638             pa_source_output_move_to(so, s, false);
639     }
640
641 }
642
643
644 /* Helper macro to reduce repetition in pa_suspend_cause_to_string().
645  * Parameters:
646  *   char *p: the current position in the write buffer
647  *   bool first: is cause_to_check the first cause to be written?
648  *   pa_suspend_cause_t cause_bitfield: the causes given to pa_suspend_cause_to_string()
649  *   pa_suspend_cause_t cause_to_check: the cause whose presence in cause_bitfield is to be checked
650  */
651 #define CHECK_CAUSE(p, first, cause_bitfield, cause_to_check) \
652     if (cause_bitfield & PA_SUSPEND_##cause_to_check) {       \
653         size_t len = sizeof(#cause_to_check) - 1;             \
654         if (!first) {                                         \
655             *p = '|';                                         \
656             p++;                                              \
657         }                                                     \
658         first = false;                                        \
659         memcpy(p, #cause_to_check, len);                      \
660         p += len;                                             \
661     }
662
663 const char *pa_suspend_cause_to_string(pa_suspend_cause_t cause_bitfield, char buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE]) {
664     char *p = buf;
665     bool first = true;
666
667     CHECK_CAUSE(p, first, cause_bitfield, USER);
668     CHECK_CAUSE(p, first, cause_bitfield, APPLICATION);
669     CHECK_CAUSE(p, first, cause_bitfield, IDLE);
670     CHECK_CAUSE(p, first, cause_bitfield, SESSION);
671     CHECK_CAUSE(p, first, cause_bitfield, PASSTHROUGH);
672     CHECK_CAUSE(p, first, cause_bitfield, INTERNAL);
673     CHECK_CAUSE(p, first, cause_bitfield, UNAVAILABLE);
674
675     if (p == buf) {
676         memcpy(p, "(none)", 6);
677         p += 6;
678     }
679
680     *p = 0;
681
682     return buf;
683 }
684
685 #ifdef __TIZEN__
686 void pa_core_dump_sink_inputs(pa_core *core) {
687     pa_sink_input *si = NULL;
688     uint32_t idx = 0;
689     unsigned int i = 0;
690
691     if (pa_idxset_isempty(core->sink_inputs)) {
692         pa_log_warn("No sink-input to dump");
693         return;
694     }
695
696     PA_IDXSET_FOREACH(si, core->sink_inputs, idx)
697         pa_sink_input_dump_info(si, ++i, pa_idxset_size(core->sink_inputs));
698 }
699
700 void pa_core_dump_source_outputs(pa_core *core) {
701     pa_source_output *so = NULL;
702     uint32_t idx = 0;
703     unsigned int i = 0;
704
705     if (pa_idxset_isempty(core->source_outputs)) {
706         pa_log_warn("No source-output to dump");
707         return;
708     }
709
710     PA_IDXSET_FOREACH(so, core->source_outputs, idx)
711         pa_source_output_dump_info(so, ++i, pa_idxset_size(core->source_outputs));
712 }
713
714 #endif